Note to the reader

markdown comments noted by the student/author (John Leonard) are highlighted in red. The final section of the document (section 7) contains the informal written report <>

Task 1: Get Started with R



Introduction Email

FROM.: Danielle Sherman
Subject: Get Started with R Hello,

Although we’re generally happy with using RapidMiner as an analytics tool, many people in the industry seem to be moving to R, an open-source statistical programming language and analytics environment that is supported by a huge community of developers. Based on the excellent analysis work you have done thus far I have decided to ask you to explore introducing R into our current processes. To help you get started, I have obtained a walkthrough tutorial in R for you and a script of code to practice with.

Here are the things I would like you to do:

  • Learn what R and RStudio are.
  • Install R and RStudio.
  • Learn how to work within RStudio.
  • Upload a data set into RStudio.
  • Install packages into RStudio.
  • Call a package in RStudio.
  • Perform basic exploratory data analysis.
  • Preprocess data.
  • Create test and training sets using your data.
  • Develop a linear regression model
  • Evaluate your model.
  • Use your model to make to make predictions.

I have attached the data sets that you’ll be using for this task. I’ll be expecting a report on your experience in a few days.

Thanks, Danielle

Danielle Sherman Chief Technology Officer Blackwell Electronics www.blackwellelectronics.com

Link:R Tutorial Data



Plan of Attack

Introduction

Danielle has asked you to install R and R Studio on your machine, then to work through a tutorial to learn the basics of analytics and visualization using R. Within your tutorial, you’ll be working through a regression analysis using a linear regression model. In this simple analysis, you’ll be predicting distances through the speed of certain cars. The dataset that you’ll be using through this analysis is the cars.csv file, which is located within Danielle’s email.

Blackwell believes that the best way to learn is through trial and error, so after your tutorial, you’ll be asked to test your knowledge through running a script of code. The dataset that you’ll be using through this analysis is the iris.csv file, which is also in the zip file attached to Danielle’s email. Don’t be too surprised if you encounter errors or warning messages. Take a few deep breaths and embrace the errors!

Once you’ve completed both tasks, you’ll submit an informal report to Danielle about your experience switching to R, the errors that you encountered in the task, the reasons behind them, and how you overcame them. You should also include discussion of the outcomes your model predicted.

1. Install R and R Studio

What is R?

R is a programming language for statistical use and data visualization. R is an interpreted language, which means you can run a line of code and receive an instant output.

How will I be learning R?

In this course, you will be using and learning R within the RStudio environment. RStudio is an Integrated Development Environment (a graphical user interface for R development.) Some Data Analyst believes that RStudio makes life easier for working with R, but you can also run R through a Command Line Interface.

R and RStudio are both open-source software, which means each piece of software is free and has active user and development communities. Because of these communities, a lot of resources have been created by fellow R programmers. Some examples are Stack-Exchange, Stack Overflow, etc.

Once you’ve installed R and RStudio you will be ready to move on to the next task.

How do I download R and RStudio?

Here is the website where you download R. https://cran.r-project.org/

How do I install RStudio? Here is where you can download RStudio. https://www.rstudio.com/products/rstudio/download/

2. Get to Know R Studio

Within this section of the tutorial, you will learn RStudio’s general layout and the functionality that each portion of the interface provides.

Becoming Familiar with RStudio

Before you begin learning R, let’s open RStudio within your computer to take a look at how to work RStudio. After opening the program you will see four sections within the interface.

The 1st Section (Upper left box) is the R Script box. The R Script box is 1 of the 2 places that you can type in your code. The advantages of the R Script box are that you can edit your code, save your code (by clicking the blue floppy disk icon), run your code, and you can have several tabs open simultaneously.

In addition to typing your code, you can also view the data you’re working with. For example:

The 2nd section (lower left) is called the Console. The Console is the 2nd place you can type and run your code. It is also where you’ll see which R version you are running (in the red box), which folder you’re working in on your computer (in blue), and where you’ll see the output of your code.

The 3rd section (upper right box) contains the Environment tab and the History tab. The History tab stores all code that you’ve created during your session. (It does not store outputs.) Within your Environment tab you can upload your dataset and save your environment.

In the 4th section (bottom right box), there are five tabs. The first tab, Files, displays the files and folders within your computer/RStudio. The second tab, Plots, is where you’ll be able to see, save, and export the plots your produce. The third tab, Packages, shows the packages that you’ve downloaded or that are available to download. The fourth tab, Help, is where you can go to receive additional documentation concerning certain topics and packages. The fifth tab, Viewer, is used to for viewing web content. (We won’t be using much of this tab during this tutorial.)

You’re now ready to move on to the next task to begin learning R. If you’d like additional information on the RStudio IDE, here is a helpful cheat sheet. https://www.rstudio.com/wp-content/uploads/2016/01/rstudio-IDE-cheatsheet.pdf

3. Walk Through the R Tutorial - Part 1

Within this section of the tutorial, you will learn how to start a new R Studio project, install packages, call on a package (library) and upload a dataset into RStudio.

Reading the Working with Projects in RStudio section of the Resources will help with the next steps.

Starting a new project

R Studio has a system for organizing your projects and keeping all of your work within its own working directory, workspace, history, and source documents. In order to work effectively and efficiently in R Studio it is important that you always start a new R Studio project whenever you start a new task. This will keep your work separate and make the work portable and easy to share with your classmates and your mentor. Here are the steps you should always follow when starting a new project in R Studio:

RStudio projects are associated with R working directories. You can create an RStudio project:

  • In a brand new directory
  • In an existing directory where you already have R code and data
  • By cloning a version control (Git or Subversion) repository To create a new project use the New Project command (available on the RStudio File menu and on the global toolbar):

When a new project is created RStudio the following takes place:

  1. Creates a project file (with an .Rproj extension) within the project directory. This file contains various project options (discussed below) and can also be used as a shortcut for opening the project directly from the filesystem.
  2. Creates a hidden directory (named .Rproj.user) where project-specific temporary files (e.g. auto-saved source documents, window-state, etc.) are stored. This directory is also automatically added to .Rbuildignore, .gitignore, etc. if required.
  3. Loads the project into RStudio and display its name in the Projects toolbar (which is located on the far right side of the main toolbar)

Installing Packages

How to install packages in RStudio: The package that you will need for this part of the tutorial is the readr package. The readr package is designed to read files, like csv files. (Although we will only be going over one alternative, there are multiple ways to install a package.)

You will be using the install.packages() function to install the readr package.

install.packages('readr')
Error in install.packages : Updating loaded packages

TIP: If you are typing in the R Script pane, press COMMAND + ENTER to run your code.

Calling on a Package

After the installation of the package, you need to call on it to have access to its functions. There are multiple ways to call a library. To see one of them, type this code line into your R Script OR Console section.

library(readr) 

Uploading Your Data

Now it’s time to upload the dataset that you’ll be using during this tutorial (the .CSV file) into RStudio. When you perform this action, you are creating a data frame, which means that all of the data that is stored within the data frame is separated by columns. Like the previous steps, there are multiple ways to upload a dataset. Here is one option.

TIP: Your dataset is cars.csv, which is located within the zip file attached to Danielle’s email, but you’ll want to create a name for your dataset. You will also need to make sure that you read how to set up your working environment or this line of code will not work.

df_cars<- read_csv("R Tutorial Data Sets/cars.csv");
Parsed with column specification:
cols(
  `name of car` = col_character(),
  `speed of car` = col_double(),
  `distance of car` = col_double()
)
View(df_cars)
print(df_cars)
df_iris<- read_csv("R Tutorial Data Sets/iris.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_double(),
  Sepal.Length = col_double(),
  Sepal.Width = col_double(),
  Petal.Length = col_double(),
  Petal.Width = col_double(),
  Species = col_character()
)
df_iris <- subset(df_iris, select = -c(X1))
View(df_iris)
print(df_iris)

4. Walk Through the R Tutorial - Part 2

Within this section of the tutorial, you will learn how to get to know your data, preprocess your data, and create training and test sets within RStudio.

Getting to Know Your Data

Once you’ve uploaded your dataset, it’s always a good idea to get to know your data. Here are some helpful functions that will help you get acquainted with your data:

Get_to_know_data <- function(DatasetName){
  print(paste("GET TO KNOW: ",deparse(substitute(DatasetName))))
  
  print('fetching attributes...')
  print(attributes(DatasetName) )#List your attributes within your data set.
  
  print('fetching summary...')
  print(summary(DatasetName) )#Prints the min, max, mean, median, and quartiles of each attribute.
  
  print('fetching data structure...')
  print(str(DatasetName) )#Displays the structure of your data set.
  
  print('fetching data attribute names...')
  print(names(DatasetName) )#Names your attributes within your data set.
  #DatasetName$ColumnName #Will print out the instances within that particular column in your data set.
}
Get_to_know_data(df_cars)
[1] "GET TO KNOW:  df_cars"
[1] "fetching attributes..."
$names
[1] "name of car"     "speed of car"    "distance of car"

$class
[1] "spec_tbl_df" "tbl_df"      "tbl"         "data.frame" 

$row.names
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
[30] 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

$spec
cols(
  `name of car` = col_character(),
  `speed of car` = col_double(),
  `distance of car` = col_double()
)

[1] "fetching summary..."
 name of car         speed of car  distance of car 
 Length:50          Min.   : 4.0   Min.   :  2.00  
 Class :character   1st Qu.:12.0   1st Qu.: 26.00  
 Mode  :character   Median :15.0   Median : 36.00  
                    Mean   :15.4   Mean   : 42.98  
                    3rd Qu.:19.0   3rd Qu.: 56.00  
                    Max.   :25.0   Max.   :120.00  
[1] "fetching data structure..."
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    50 obs. of  3 variables:
 $ name of car    : chr  "Ford" "Jeep" "Honda" "KIA" ...
 $ speed of car   : num  4 4 7 7 8 9 10 10 10 11 ...
 $ distance of car: num  2 4 10 10 14 16 17 18 20 20 ...
 - attr(*, "spec")=
  .. cols(
  ..   `name of car` = col_character(),
  ..   `speed of car` = col_double(),
  ..   `distance of car` = col_double()
  .. )
NULL
[1] "fetching data attribute names..."
[1] "name of car"     "speed of car"    "distance of car"
Get_to_know_data(df_iris)
[1] "GET TO KNOW:  df_iris"
[1] "fetching attributes..."
$names
[1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width"  "Species"     

$class
[1] "tbl_df"     "tbl"        "data.frame"

$row.names
  [1]   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21
 [22]  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42
 [43]  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63
 [64]  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84
 [85]  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 101 102 103 104 105
[106] 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
[127] 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
[148] 148 149 150

[1] "fetching summary..."
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width      Species         
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100   Length:150        
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300   Class :character  
 Median :5.800   Median :3.000   Median :4.350   Median :1.300   Mode  :character  
 Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199                     
 3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800                     
 Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500                     
[1] "fetching data structure..."
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   150 obs. of  5 variables:
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 $ Species     : chr  "setosa" "setosa" "setosa" "setosa" ...
NULL
[1] "fetching data attribute names..."
[1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width"  "Species"     

Plotting is also a helpful way to view your dataset. Here are some helpful functions that will help you get acquainted with your data visually:

TIP: Your columns must be in numeric form to perform these plots.

plot_summary_of_data<-function(DatasetName,x_index=1){
  
  column_names = names(DatasetName)
  
  subplot_cols = 2
  subplot_rows = 2
  par(mfrow=c(subplot_rows,subplot_cols))  
  
  x <- unlist(DatasetName[,x_index])
  x_header = column_names[x_index]
  
  for(i in 1:length(column_names)){
    y <- unlist(DatasetName[,i])
    y_header = column_names[i]
    try(hist(y, main = paste(y_header, "Histogram"), xlab = y_header ),silent=TRUE)#Histogram Plot
  }
  
  for(i in 1:length(column_names)){
    
    if(i != x_index) {
    y <- unlist(DatasetName[,i])
    y_header = column_names[i]
    
    try(plot(x,y, xlab = x_header, ylab = y_header),silent=TRUE)  #Scatter (Box) Plot
    } 
  }
  
  #Normal Quantile Plot- is a way to see if your data is normally distributed.
  for(i in 1:length(column_names)){
    
    y <- unlist(DatasetName[,i])
    y_header = column_names[i]
    
 try(qqnorm(y,main = paste(y_header, " Normal Q-Q Plot")),silent=TRUE) ##Normal Quantile Plot
  }
}
plot_summary_of_data(df_cars,x_index=2)

plot_summary_of_data(df_iris,x_index=1)

Preprocessing your Data

Preprocessing, also known as data cleaning, is a vital step in your analysis process. Some reasons to prepare your data is so it can be analyzed, it might be noisy data (missing values/outliers), it could have attributes that aren’t helpful, etc. There are many steps that one may take when preparing your data, we will only be discussing a handful at a high-level view.

When working with data, you will need to be aware of what a vector is. A vector is a sequence of the same data type.

Here are the data types that you will encounter when working in R:

  • Numeric- Numbers with decimals. (Ex: 1.0, 10.5, 4.5, etc.)
  • Integer Data- Whole numbers (Ex: 11, 45, 78, etc.)
  • Factor Data- Categorical data (Ex: Red, Blue, Green, Purple, etc.)
  • Ordinal Data- Ordered data (Ex: educational levels, temperature, etc.)
  • Character Data- String values, which are characters (words) with quotes around them. (Ex: “Red”, “Blue”, “Green”, “Purple”, etc.)
  • Logical- TRUE or TRUE (Always capitalize TRUE or FALSE)

Do you see any data types that need changing within your data set? If so, how do you convert data types? Converting data types is a helpful skill to learn for this tutorial and future analyses. Here is an example of how one would change a column’s data type within a data set:

#DatasetName$ColumnName<-as.typeofdata(DatasetName$ColumnName)

Do the columns/attributes within your dataset need renaming?

the numeric data seems fine (all ‘dbl’), however the character data should be changed to ‘Factor’ data as it is all categorical

df_cars$"name of car"<-as.factor(df_cars$"name of car")
df_iris$"Species"<-as.factor(df_iris$"Species")

To rename the attributes/columns in your dataset, you’ll want to use the c() function, specifying a name for each column.

reformat_column_headers <- function(DatasetName){
  column_names = names(DatasetName)
  
  column_names_reformated = gsub(" ","_",fixed=TRUE,column_names)
  
  names(DatasetName)<-c(column_names_reformated)
  
  return(DatasetName)
}
df_cars = reformat_column_headers(df_cars)
print(names(df_cars))
[1] "name_of_car"     "speed_of_car"    "distance_of_car"
df_iris = reformat_column_headers(df_iris)
print(names(df_iris))
[1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width"  "Species"     

Do any of your variables have missing values? How do you know if your dataset has any missing values? If you do not address missing values certain functions will not work properly, so it’s smart to start the practice checking for missing values. R labels missing as NA (Not Available). Here are two ways to know if you have any missing values:

summary(df_cars) #Will count how many NA’s you have.
  name_of_car  speed_of_car  distance_of_car 
 Dodge  : 3   Min.   : 4.0   Min.   :  2.00  
 Honda  : 3   1st Qu.:12.0   1st Qu.: 26.00  
 Jeep   : 3   Median :15.0   Median : 36.00  
 KIA    : 3   Mean   :15.4   Mean   : 42.98  
 Acura  : 2   3rd Qu.:19.0   3rd Qu.: 56.00  
 Audi   : 2   Max.   :25.0   Max.   :120.00  
 (Other):34                                  
summary(df_iris)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100   setosa    :50  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300   versicolor:50  
 Median :5.800   Median :3.000   Median :4.350   Median :1.300   virginica :50  
 Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199                  
 3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800                  
 Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500                  
is.na(df_cars) #Will show your NA’s through logical data. (TRUE if it’s missing, FALSE if it’s not.)
      name_of_car speed_of_car distance_of_car
 [1,]       FALSE        FALSE           FALSE
 [2,]       FALSE        FALSE           FALSE
 [3,]       FALSE        FALSE           FALSE
 [4,]       FALSE        FALSE           FALSE
 [5,]       FALSE        FALSE           FALSE
 [6,]       FALSE        FALSE           FALSE
 [7,]       FALSE        FALSE           FALSE
 [8,]       FALSE        FALSE           FALSE
 [9,]       FALSE        FALSE           FALSE
[10,]       FALSE        FALSE           FALSE
[11,]       FALSE        FALSE           FALSE
[12,]       FALSE        FALSE           FALSE
[13,]       FALSE        FALSE           FALSE
[14,]       FALSE        FALSE           FALSE
[15,]       FALSE        FALSE           FALSE
[16,]       FALSE        FALSE           FALSE
[17,]       FALSE        FALSE           FALSE
[18,]       FALSE        FALSE           FALSE
[19,]       FALSE        FALSE           FALSE
[20,]       FALSE        FALSE           FALSE
[21,]       FALSE        FALSE           FALSE
[22,]       FALSE        FALSE           FALSE
[23,]       FALSE        FALSE           FALSE
[24,]       FALSE        FALSE           FALSE
[25,]       FALSE        FALSE           FALSE
[26,]       FALSE        FALSE           FALSE
[27,]       FALSE        FALSE           FALSE
[28,]       FALSE        FALSE           FALSE
[29,]       FALSE        FALSE           FALSE
[30,]       FALSE        FALSE           FALSE
[31,]       FALSE        FALSE           FALSE
[32,]       FALSE        FALSE           FALSE
[33,]       FALSE        FALSE           FALSE
[34,]       FALSE        FALSE           FALSE
[35,]       FALSE        FALSE           FALSE
[36,]       FALSE        FALSE           FALSE
[37,]       FALSE        FALSE           FALSE
[38,]       FALSE        FALSE           FALSE
[39,]       FALSE        FALSE           FALSE
[40,]       FALSE        FALSE           FALSE
[41,]       FALSE        FALSE           FALSE
[42,]       FALSE        FALSE           FALSE
[43,]       FALSE        FALSE           FALSE
[44,]       FALSE        FALSE           FALSE
[45,]       FALSE        FALSE           FALSE
[46,]       FALSE        FALSE           FALSE
[47,]       FALSE        FALSE           FALSE
[48,]       FALSE        FALSE           FALSE
[49,]       FALSE        FALSE           FALSE
[50,]       FALSE        FALSE           FALSE

How to address missing values? There are multiple ways to confront missing values in your dataset – all depend on how much they will affect your dataset. Here are a few options:

  • Remove any observations containing missing data. (If the missing data is less than 10% of the total data and only after comparing the min/max of all the features both with and without the missing data.)
na.omit(df_cars)#Drops any rows with missing values and omits them forever.
na.omit(df_iris)
#na.exclude(DatasetName$ColumnName)#Drops any rows with missing values, but keeps track of where they were.

Replace the missing values with the mean, which is common technique, but something to use with care with as it can skew the data.

#DatasetName$ColumnName[is.na(DatasetName$ColumnName)]<-mean(DatasetName$ColumnName,na.rm = TRUE)

Creating Testing and Training Sets

Once you’ve preprocessed your dataset, it’s now time to create training and testing sets for the linear regression model you will be creating.

How to begin? In order to create your training and testing sets, you need to use the set.seed() function. The seed is a number that you choose for a starting point used to create a sequence of random numbers. It is also helpful for others who want to recreate your same results. Here is the function:

TIP: A common set.seed number is 123. To use the same set of random numbers, you’ll want to use the same seed number throughout your modeling process.

set.seed(1)

How do you split the data into training and test sets? You’ll now want to split your data into two sets for modeling. One is the training set and the other one being the test set. A common split is 70/30, which means that 70% of the data will be the training set’s size and 30% of the data will be the test set’s size. You will be using the 70/30 split, but another common split is 80/20.

Setting the training set’s size and the testing set’s size can be done by performing these two lines of code. These two lines calculate the sizes of each set but do not create the sets:

train_test_size<-function(DatasetName,training_size_ratio=0.7){
  trainSize<-round(nrow(DatasetName)*training_size_ratio) 
  testSize<-nrow(DatasetName)-trainSize
  df_out = data.frame('trainSize'=c(trainSize),'testSize'=c(testSize))
  return(df_out)
}
train_test_size(df_cars,0.7)
train_test_size(df_iris,0.7)

How do you create the training and test sets? It’s now time for you to create the training and test sets. We also want these sets to be in a randomized order, which will create the most optimal model.

To perform this, you need to run these lines of code. Type in this code into R Script or Console:

train_test_split<-function(DatasetName,training_size_ratio=0.7){
                           
  df_train_test_size <- train_test_size(df_cars,training_size_ratio)  #get train test sizes
  trainSize<-df_train_test_size[1,1]
  
  training_indices<-sample(seq_len(nrow(DatasetName)),size = trainSize)
  
  trainSet<-DatasetName[training_indices,]
  
  testSet<-DatasetName[-training_indices,] 
  
  return(list(trainSet,testSet))
}
list_df_train_test_sets = train_test_split(df_cars,0.7)
df_trainSet = list_df_train_test_sets[[1]]
print(df_trainSet)
df_testSet = list_df_train_test_sets[[2]]
print(df_testSet)

5. Walk Through the R Tutorial - Part 3

Within this section of the tutorial, you will learn how to create a linear regression model, understand its output and use the prediction function.

Linear Regression Model

You’re now ready to run your data through your modeling algorithm. The model that we will be using is the Linear Regression Model, which is helpful when trying to discover the relationship between two variables. These two variables represent the X and Y within the linear equation. The X variable is the predictor variable, also known as the independent variable because it doesn’t depend on other attributes while making predictions. Y is the response variable, also known as the dependent variable because its value depends on the other variables. (We will be keeping this at a high level. If you’d like to discover more about this equation, please feeI free to do your own research.) In our case, these two variables will be Speed and Distance. We are trying to predict Distance, so it is our dependent/response/Y variable. Speed is our independent/predictor/X variable.

plot(df_cars$'speed_of_car',df_cars$'distance_of_car',xlab = 'speed_of_car', ylab = 'distance_of_car',main='training+test set')

plot(df_trainSet$'speed_of_car',df_trainSet$'distance_of_car',xlab = 'speed_of_car', ylab = 'distance_of_car',main='df_trainSet')

plot(df_testSet$'speed_of_car',df_testSet$'distance_of_car',xlab = 'speed_of_car', ylab = 'distance_of_car',main='testSet')

To create this model, we will be using the linear model function – lm(). Here is the basic line of code for the linear model function.

lm_cars_speed_vs_distance<-lm(distance_of_car ~ speed_of_car, df_trainSet)

Did you create an optimal model? To see key metrics of your model, type in this code into R Script or Console

summary(lm_cars_speed_vs_distance)

Call:
lm(formula = distance_of_car ~ speed_of_car, data = df_trainSet)

Residuals:
    Min      1Q  Median      3Q     Max 
-9.3709 -4.9106 -0.8151  1.4703 29.2644 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -32.2791     3.8682  -8.345 1.22e-09 ***
speed_of_car   4.9206     0.2304  21.358  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 7.295 on 33 degrees of freedom
Multiple R-squared:  0.9325,    Adjusted R-squared:  0.9305 
F-statistic: 456.2 on 1 and 33 DF,  p-value: < 2.2e-16

The two metrics that we will be discussing are:

  • Multiple R-squared- How well the regression line fits the data (1 means it’s a perfect fit).
  • p-value - Tells you how much the Independent Variable/Predictor affects the Dependent Variable/Response/. A p-value of more than 0.05 means the Independent Variable has no effect on the Dependent Variable; less than 0.05 means the relationship is statistically significant.
  • If you’d like to learn more about Adjusted R-squared and the F-statistic, see the Resources tab. For now, you will be leaving this model as is. You will learn how to adjust your model’s parameters to create the most optimal model in later courses.

Predictions

The next step is to predict the cars distances through the speed of the cars. To do this, we’ll be using the prediction function – predict()

predict_lm_cars_vs_distance <- predict(lm_cars_speed_vs_distance,df_testSet)
print("predictions:")
[1] "predictions:"
predict_lm_cars_vs_distance
         1          2          3          4          5          6          7          8 
-12.596735   7.085622  12.006212  21.847390  31.688568  31.688568  36.609158  36.609158 
         9         10         11         12         13         14         15 
 41.529747  41.529747  56.291515  56.291515  61.212104  61.212104  80.894461 
plot(df_trainSet$speed_of_car, df_trainSet$distance_of_car, xlab = 'speed of car', ylab = 'distance of car', col='red',pch = 19)
abline(lm(distance_of_car ~ speed_of_car, df_trainSet),col='red')
points(df_testSet$speed_of_car, df_testSet$distance_of_car,col='blue')
legend("topleft", legend=c("TrainSet & Linear Model","TestSet"),
       col=c("red","blue"),lty=1, cex=.9)

TIP: Make a note of your predictions. (You will need to include them in your informal report to Blackwell.)

Find the Errors in an R Scripts

Congrats, you’ve now completed a Regression Analysis in R!

Blackwell believes that reading and deciphering code is just as important as coding itself. Therefore, it’s now time to test your new-found knowledge of R by running a script of code. Beware that you will be encountering errors and warnings, but you have the power to overcome these!

What does an error look like? An error message will be in red and will prevent you from going further due to a mistake in the previous line or lines of code.

What does a warning message look like? A warning message will also be in red. You will still be able to continue, but it might or might not affect your analysis in the long run.

You’ve encountered an error/warning message, what now?

Review your tutorial. Check your spelling/spacing. Research the error/warning message — Has anyone else encountered this error? TIP: REMEMBER:

Don’t forget your analysis goal! If you are typing in the Console, press ENTER to see run your code. If you are typing in the R Script, press COMMAND + ENTER to see run your code. The dataset that is discussed in the code concerns the Iris flower, which is within the zip file attached to Danielle’s email. The analysis goal is to predict a petal’s length using the petal’s width.

Here is the script that you will run (and fix any errors it contains):

install.packages(readr) # Original Code
Error in install.packages : object 'readr' not found
install.packages('readr') # New Code | Note: make sure you use quotes when calling a package
#library(“readr”) # Original Code
#Error: unexpected input in "library(�"
library(readr) # New Code | Note: reference by unquoted name when reading library package.
#IrisDataset <- read.csv(iris.csv) # Original Code
#Error in read.table(file = file, header = header, sep = sep, quote = quote, : object 'iris.csv' not found
IrisDataset <- read.csv("R Tutorial Data Sets/iris.csv") # New Code | Note: point to directory location using quotes
attributes(IrisDataset) #Original code
$names
[1] "X"            "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width" 
[6] "Species"     

$class
[1] "data.frame"

$row.names
  [1]   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21
 [22]  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42
 [43]  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63
 [64]  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84
 [85]  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 101 102 103 104 105
[106] 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
[127] 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
[148] 148 149 150
#summary(risDataset) #Original code
#Error in summary(risDataset) : object 'risDataset' not found
summary(IrisDataset) # new code | note: missing "I" at start of "iris..."
       X           Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
 Min.   :  1.00   Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
 1st Qu.: 38.25   1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
 Median : 75.50   Median :5.800   Median :3.000   Median :4.350   Median :1.300  
 Mean   : 75.50   Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
 3rd Qu.:112.75   3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
 Max.   :150.00   Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
       Species  
 setosa    :50  
 versicolor:50  
 virginica :50  
                
                
                
# str(IrisDatasets) # Original code
# Error in str(IrisDatasets) : object 'IrisDatasets' not found
str(IrisDataset) # new code | typo
'data.frame':   150 obs. of  6 variables:
 $ X           : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
names(IrisDataset) #original code
[1] "X"            "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width" 
[6] "Species"     
# hist(IrisDataset$Species) #original code 
# Error in hist.default(IrisDataset$Species) : 'x' must be numeric
hist(IrisDataset$Sepal.Length) #new code | histogram can only accept numeric columns

# plot(IrisDataset$Sepal.Length #original code
# Error: Incomplete expression: plot(IrisDataset$Sepal.Length #original code
plot(IrisDataset$Sepal.Length) #new code | missing end bracket

#qqnorm(IrisDataset) #original code
#Error in FUN(X[[i]], ...) : only defined on a data frame with all numeric variables
qqnorm(IrisDataset$Sepal.Length) #original code | must select a specific numeric column for Q-Q plot

IrisDataset$Species<- as.numeric(IrisDataset$Species)  # original code
set.seed(123) # original code
trainSize <- round(nrow(IrisDataset) * 0.2)# original code
trainSize
[1] 30
testSize <- nrow(IrisDataset) - trainSet # original code
testSize
testSize <- round(nrow(IrisDataset)) - trainSize # new code | replace trainSet with trainSize
testSize
[1] 120
# trainSizes # orgiinal code
# Error: object 'trainSizes' not found
trainSize # new code | drop "s" from original code
[1] 30
testSize #original code
[1] 120
trainSet <- IrisDataset[training_indices, ] #original code
#new code
training_indices<-sample(seq_len(nrow(IrisDataset)),size = trainSize) #get indices
trainSet<-IrisDataset[training_indices,]
print(trainSet)
testSet <- IrisDataset[-training_indices, ]#original code
set.seed(405)#original code
trainSet <- IrisDataset[training_indices, ] #original code (repreated previously)
testSet <- IrisDataset[-training_indices, ] #original code (repreated previously)
# LinearModel<- lm(trainSet$Petal.Width ~ testingSet$Petal.Length) #original code
# Error in eval(predvars, data, env) : object 'testingSet' not found
LinearModel<- lm(Petal.Width ~ Petal.Length, trainSet) #new code | both data sets must come from same data frame
summary(LinearModel) # original code

Call:
lm(formula = Petal.Width ~ Petal.Length, data = trainSet)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.36434 -0.15638 -0.01071  0.08594  0.60354 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)   -0.2552     0.1165  -2.191   0.0369 *  
Petal.Length   0.3827     0.0275  13.917 4.18e-14 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2368 on 28 degrees of freedom
Multiple R-squared:  0.8737,    Adjusted R-squared:  0.8692 
F-statistic: 193.7 on 1 and 28 DF,  p-value: 4.185e-14
# prediction<-predict(LinearModeltestSet) #orignal code
# Error in predict(LinearModeltestSet) : object 'LinearModeltestSet' not found
prediction<-predict(LinearModel,testSet) #new code
# predictions #original code
# Error: object 'predictions' not found
prediction #new code
        1         2         3         4         5         8         9        10        11 
0.2805356 0.2805356 0.2422673 0.3188039 0.2805356 0.3188039 0.2805356 0.3188039 0.3188039 
       12        13        15        16        17        19        20        21        22 
0.3570723 0.2805356 0.2039989 0.3188039 0.2422673 0.3953406 0.3188039 0.3953406 0.3188039 
       23        24        25        26        27        28        29        30        31 
0.1274623 0.3953406 0.4718772 0.3570723 0.3570723 0.3188039 0.2805356 0.3570723 0.3570723 
       32        34        35        37        38        39        40        41        42 
0.3188039 0.2805356 0.3188039 0.2422673 0.2805356 0.2422673 0.3188039 0.2422673 0.2422673 
       43        45        46        47        48        49        50        51        52 
0.2422673 0.4718772 0.2805356 0.3570723 0.2805356 0.3188039 0.2805356 1.5433905 1.4668539 
       53        54        55        56        57        58        59        60        62 
1.6199272 1.2755122 1.5051222 1.4668539 1.5433905 1.0076339 1.5051222 1.2372439 1.3520489 
       63        66        67        69        70        71        72        73        75 
1.2755122 1.4285855 1.4668539 1.4668539 1.2372439 1.5816589 1.2755122 1.6199272 1.3903172 
       76        78        80        81        84        85        86        87        88 
1.4285855 1.6581955 1.0841706 1.1989756 1.6964639 1.4668539 1.4668539 1.5433905 1.4285855 
       91        92        93        95        96        97        98        99       100 
1.4285855 1.5051222 1.2755122 1.3520489 1.3520489 1.3520489 1.3903172 0.8928289 1.3137806 
      101       102       103       104       105       106       107       108       109 
2.0408789 1.6964639 2.0026105 1.8878055 1.9643422 2.2704889 1.4668539 2.1556839 1.9643422 
      110       111       112       113       114       115       117       119       120 
2.0791472 1.6964639 1.7730005 1.8495372 1.6581955 1.6964639 1.8495372 2.3852939 1.6581955 
      121       123       124       125       129       131       132       133       135 
1.9260739 2.3087572 1.6199272 1.9260739 1.8878055 2.0791472 2.1939522 1.8878055 1.8878055 
      136       137       139       140       141       143       144       145       146 
2.0791472 1.8878055 1.5816589 1.8112689 1.8878055 1.6964639 2.0026105 1.9260739 1.7347322 
      147       148       149 
1.6581955 1.7347322 1.8112689 
plot(trainSet$Petal.Length, trainSet$Petal.Width, xlab = 'Petal.Length', ylab = 'Petal.Width', col='red',pch = 19)
abline(lm(Petal.Width ~ Petal.Length, trainSet),col='red')
points(testSet$Petal.Length, testSet$Petal.Width,col='blue')
legend("topleft", legend=c("TrainSet & Linear Model","TestSet"),
       col=c("red","blue"),lty=1, cex=0.9)

7. Write and Informal Report

Now that you’ve completed the R tutorial and the Find The Errors tasks, write an informal report in Word to Danielle from Blackwell.

Your report should include the following:

Your predictions concerning how far a certain car can travel based on speed. (From the R tutorial.) Your predictions concerning the petal length through using the petal’s width. (From the Find the Errors task.) The errors/warning messages that you encountered and how you overcame them. In addition, think about adding the answers to these questions within your report:

Was it straightforward to install R and RStudio? Was the tutorial useful? Would you recommend it to others? What are the main lessons you’ve learned from this experience? What recommendations would you give to other employees who need to get started using R and doing predictive analytics in R instead of Rapidminer?



Getting Started with R: Informal Report

Summary

In this task we explored using R as a tool for data science/analytics. Compared to Rapid Miner, R is significantly more powerful and flexible, though it may seem more complex to new users. In particular, utilizing R via RStudio gives the user a kind of integraded development environment (IDE) for data science. The task covered the basics of a data science pipeline: loading key modules/libraries, loading data, cleaning & preprocessing data, apply a train-test split, training a machine learning model (ML), and making a prediction using the ML model. Overall, the ability to implement these steps in code and via a computational notebook style, makes data reproducability and much more efficient. This is because the notebook style of data analysis allows you to integrate written text with code and output, allowing you to create a kind of user manual for reproducing your results. Furthermore, utilizing code to perform data analysis gives you access to useful tricks, such as for loops, if-then statements, custom functions, and more.

Results

Two data sets were analyzed: (1) “cars.csv” & (2) “iris.csv”. In both data sets, double (numeric), and categorical data columns were present, however in this task we focused only on the numeric data. Specifically, after some preliminary data exploration via scatterplots, histograms, and Q-Q plots, we performed a 70-30% train-test split and fit a linear regression model to the data sets.

In the case of “cars.csv”, we applied linear regression to predict the distance of a card, given it’s speed. The result of this linear model can be seen in the figure below. Figure 1 Here, we have highlighted the data points from the training set in red. The linear model was trained on this data set, thus the prediciton line is also colored red. The test set can be in blue. Note that the test set covers a similar distribution/range as the training set, though there are more data points. This linear model achieve an \(R^2\) score of 0.93 on the testing set, implying the a good model fit, as can be seen from the plot. It should be noted that one could likely transform the x-axis, possibly using a semi-log scale of some degree, to force the relationship between the distance of the car and the speed of the car to become event more linear. This would then give bette prediction results in the extremes of the data set and probably lead to better extrapolation

Following the “cars.csv” data analysis, we performed an analysis on the “iris.csv” data set. Part of the analysis for this data set involved debugging code provided by the instructor. This code, along with the corrected code, can be found in the “Find the Errors in an R Scripts” section of this notebook. The original code is denoted by the comment “# original code”, the resulting errors have been pasted below the original code as a comment as well, followed by the new code (#new code). Most of the errors were simply syntactic in nature. The types of errors are most easily avoided using the autofill (tab-out) functionality common to most scripting languages.

After debugging the provided code, we performed another linear regression fit, but this time to the “iris.csv” petal length vs. petal width. The results of the model are shown below, with color coding identical to the previously analyzed figure. Figure 2 Here we see a linear model appears to represent the true nature of the trend in the data better than it did in the car case, however there is also more spread in the data for a given petal length.

Concluding Comments

Overall, I found it pretty straightforward to download and install R and RStudio. Most of the activity was pretty straightforward, however I also have a lot of experience with notebooks generally and a few different coding languages, all of which have their own unique ‘features’. One thing I enjoyed the most was actually exploring beyond what the assignment explictly asked. Specifically, I found it useful looking into how to build custom functions in R and implement for loops. This was my first time using R and overall I would say it is useful for data scientists to be familiar with R, but I honestly would not recommend new data scientists spend too much time in R. This is primarily because R seems a bit clunky and less scalable/flexible compared to python. In particular, more advanced machine learning models, such as neural networks, are not commonly developed in R. Additionally, python is used to develop professional software interfaces, interact with analytical hardware machines/tools, and much more, this it is more likely that a data scientist could integrate a code into some software engineers data acquisition/analysis pipeline if it is written in R. Another complaint about R is that some of the syntax is very non-intuiative and clunky. For example, why do I need to do ‘<-’ to make expressions all the time. It would be some much nicer if I could just press “=”. Finally, the error handling in R seems much less transparent the in python. In R it isn’t immediately obvious which line of code threw an error.

All that being said, I still think knowing R is useful because it is still a popular language in data science and being able to “speak” many different languages is always useful.

As for recommendations to others in learning R, I would say with coding in general the key is to just practice & practice. Things feel slow and uncomfortable at first, but there is a large community that can help you solve almost any problem and the only thing stopping you from building whatever code or ML model you want is just time and perseverance.

LS0tCnRpdGxlOiAiQzAyVDAxX0dldHRpbmdfU3RhcnRlZF93aXRoX1IiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KKioqCioqKgojIyMjIE5vdGUgdG8gdGhlIHJlYWRlcgoKPiA8c3BhbiBzdHlsZT0nY29sb3I6cmVkJz4gbWFya2Rvd24gY29tbWVudHMgbm90ZWQgYnkgdGhlIHN0dWRlbnQvYXV0aG9yIChKb2huIExlb25hcmQpIGFyZSBoaWdobGlnaHRlZCBpbiByZWQuIFRoZSBmaW5hbCBzZWN0aW9uIG9mIHRoZSBkb2N1bWVudCAoc2VjdGlvbiA3KSBjb250YWlucyB0aGUgaW5mb3JtYWwgd3JpdHRlbiByZXBvcnQgPFxzcGFuPgoKIyBUYXNrIDE6IEdldCBTdGFydGVkIHdpdGggUgoKKioqCioqKgojIyBJbnRyb2R1Y3Rpb24gRW1haWwKX19GUk9NLjpfXyBEYW5pZWxsZSBTaGVybWFuIDxicj4KX19TdWJqZWN0Ol9fIEdldCBTdGFydGVkIHdpdGggUgpIZWxsbywKIApBbHRob3VnaCB3ZSdyZSBnZW5lcmFsbHkgaGFwcHkgd2l0aCB1c2luZyBSYXBpZE1pbmVyIGFzIGFuIGFuYWx5dGljcyB0b29sLCBtYW55IHBlb3BsZSBpbiB0aGUgaW5kdXN0cnkgc2VlbSB0byBiZSBtb3ZpbmcgdG8gUiwgYW4gb3Blbi1zb3VyY2Ugc3RhdGlzdGljYWwgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UgYW5kIGFuYWx5dGljcyBlbnZpcm9ubWVudCB0aGF0IGlzIHN1cHBvcnRlZCBieSBhIGh1Z2UgY29tbXVuaXR5IG9mIGRldmVsb3BlcnMuIEJhc2VkIG9uIHRoZSBleGNlbGxlbnQgYW5hbHlzaXMgd29yayB5b3UgaGF2ZSBkb25lIHRodXMgZmFyIEkgaGF2ZSBkZWNpZGVkIHRvIGFzayB5b3UgdG8gZXhwbG9yZSBpbnRyb2R1Y2luZyBSIGludG8gb3VyIGN1cnJlbnQgcHJvY2Vzc2VzLiBUbyBoZWxwIHlvdSBnZXQgc3RhcnRlZCwgSSBoYXZlIG9idGFpbmVkIGEgd2Fsa3Rocm91Z2ggdHV0b3JpYWwgaW4gUiBmb3IgeW91IGFuZCBhIHNjcmlwdCBvZiBjb2RlIHRvIHByYWN0aWNlIHdpdGguCiAKSGVyZSBhcmUgdGhlIHRoaW5ncyBJIHdvdWxkIGxpa2UgeW91IHRvIGRvOiAKIAoqIExlYXJuIHdoYXQgUiBhbmQgUlN0dWRpbyBhcmUuCiogSW5zdGFsbCBSIGFuZCBSU3R1ZGlvLgoqIExlYXJuIGhvdyB0byB3b3JrIHdpdGhpbiBSU3R1ZGlvLgoqIFVwbG9hZCBhIGRhdGEgc2V0IGludG8gUlN0dWRpby4KKiBJbnN0YWxsIHBhY2thZ2VzIGludG8gUlN0dWRpby4KKiBDYWxsIGEgcGFja2FnZSBpbiBSU3R1ZGlvLgoqIFBlcmZvcm0gYmFzaWMgZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcy4KKiBQcmVwcm9jZXNzIGRhdGEuCiogQ3JlYXRlIHRlc3QgYW5kIHRyYWluaW5nIHNldHMgdXNpbmcgeW91ciBkYXRhLgoqIERldmVsb3AgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbAoqIEV2YWx1YXRlIHlvdXIgbW9kZWwuCiogVXNlIHlvdXIgbW9kZWwgdG8gbWFrZSB0byBtYWtlIHByZWRpY3Rpb25zLgogCkkgaGF2ZSBhdHRhY2hlZCB0aGUgZGF0YSBzZXRzIHRoYXQgeW91J2xsIGJlIHVzaW5nIGZvciB0aGlzIHRhc2suIEknbGwgYmUgZXhwZWN0aW5nIGEgcmVwb3J0IG9uIHlvdXIgZXhwZXJpZW5jZSBpbiBhIGZldyBkYXlzLgogClRoYW5rcywKRGFuaWVsbGUKIApEYW5pZWxsZSBTaGVybWFuCkNoaWVmIFRlY2hub2xvZ3kgT2ZmaWNlcgpCbGFja3dlbGwgRWxlY3Ryb25pY3MKd3d3LmJsYWNrd2VsbGVsZWN0cm9uaWNzLmNvbQogCl9fW0xpbms6UiBUdXRvcmlhbCBEYXRhXShodHRwczovL3MzLmFtYXpvbmF3cy5jb20vZ2JzdG9vbC9lbWFpbHMvMjc4NC9SJTIwVHV0b3JpYWwlMjBEYXRhLnppcD9BV1NBY2Nlc3NLZXlJZD1BS0lBSkJJWkxNSlEyTzZES0lBQSZFeHBpcmVzPTE1NDcxMTA4MDAmU2lnbmF0dXJlPWUxQzhUYUV3TExiYTMwbmo0b1N4bWVtZXZhVSUzRClfXwoKKioqCioqKgojIyBQbGFuIG9mIEF0dGFjawojIyMgSW50cm9kdWN0aW9uCkRhbmllbGxlIGhhcyBhc2tlZCB5b3UgdG8gaW5zdGFsbCBSIGFuZCBSIFN0dWRpbyBvbiB5b3VyIG1hY2hpbmUsIHRoZW4gdG8gd29yayB0aHJvdWdoIGEgdHV0b3JpYWwgdG8gbGVhcm4gdGhlIGJhc2ljcyBvZiBhbmFseXRpY3MgYW5kIHZpc3VhbGl6YXRpb24gdXNpbmcgUi4gX19XaXRoaW4geW91ciB0dXRvcmlhbCwgeW914oCZbGwgYmUgd29ya2luZyB0aHJvdWdoIGEgcmVncmVzc2lvbiBhbmFseXNpcyB1c2luZyBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLl9fIEluIHRoaXMgc2ltcGxlIGFuYWx5c2lzLCB5b3XigJlsbCBiZSBfX3ByZWRpY3RpbmcgZGlzdGFuY2VzIHRocm91Z2ggdGhlIHNwZWVkIG9mIGNlcnRhaW4gY2Fycy5fXyBUaGUgZGF0YXNldCB0aGF0IHlvdeKAmWxsIGJlIHVzaW5nIHRocm91Z2ggdGhpcyBhbmFseXNpcyBpcyB0aGUgX19jYXJzLmNzdiBmaWxlX18sIHdoaWNoIGlzIGxvY2F0ZWQgd2l0aGluIERhbmllbGxl4oCZcyBlbWFpbC4KCkJsYWNrd2VsbCBiZWxpZXZlcyB0aGF0IHRoZSBiZXN0IHdheSB0byBsZWFybiBpcyB0aHJvdWdoIHRyaWFsIGFuZCBlcnJvciwgc28gX19hZnRlciB5b3VyIHR1dG9yaWFsLCB5b3XigJlsbCBiZSBhc2tlZCB0byB0ZXN0IHlvdXIga25vd2xlZGdlIHRocm91Z2ggcnVubmluZyBhIHNjcmlwdCBvZiBjb2RlLiBUaGUgZGF0YXNldCB0aGF0IHlvdeKAmWxsIGJlIHVzaW5nIHRocm91Z2ggdGhpcyBhbmFseXNpcyBpcyB0aGUgaXJpcy5jc3YgZmlsZSxfXyB3aGljaCBpcyBhbHNvIGluIHRoZSB6aXAgZmlsZSBhdHRhY2hlZCB0byBEYW5pZWxsZeKAmXMgZW1haWwuIERvbuKAmXQgYmUgdG9vIHN1cnByaXNlZCBpZiB5b3UgZW5jb3VudGVyIGVycm9ycyBvciB3YXJuaW5nIG1lc3NhZ2VzLiBUYWtlIGEgZmV3IGRlZXAgYnJlYXRocyBhbmQgZW1icmFjZSB0aGUgZXJyb3JzIQoKT25jZSB5b3XigJl2ZSBjb21wbGV0ZWQgYm90aCB0YXNrcywgX195b3UnbGwgc3VibWl0IGFuIGluZm9ybWFsIHJlcG9ydCB0byBEYW5pZWxsZSBhYm91dCB5b3VyIGV4cGVyaWVuY2Ugc3dpdGNoaW5nIHRvIFIsIHRoZSBlcnJvcnMgdGhhdCB5b3UgZW5jb3VudGVyZWQgaW4gdGhlIHRhc2ssIHRoZSByZWFzb25zIGJlaGluZCB0aGVtLCBhbmQgaG93IHlvdSBvdmVyY2FtZSB0aGVtLiBZb3Ugc2hvdWxkIGFsc28gaW5jbHVkZSBkaXNjdXNzaW9uIG9mIHRoZSBvdXRjb21lcyB5b3VyIG1vZGVsIHByZWRpY3RlZC5fXwoKIyMjIDEuIEluc3RhbGwgUiBhbmQgUiBTdHVkaW8KIyMjIyBXaGF0IGlzIFI/ClIgaXMgYSBwcm9ncmFtbWluZyBsYW5ndWFnZSBmb3Igc3RhdGlzdGljYWwgdXNlIGFuZCBkYXRhIHZpc3VhbGl6YXRpb24uIFIgaXMgYW4gaW50ZXJwcmV0ZWQgbGFuZ3VhZ2UsIHdoaWNoIG1lYW5zIHlvdSBjYW4gcnVuIGEgbGluZSBvZiBjb2RlIGFuZCByZWNlaXZlIGFuIGluc3RhbnQgb3V0cHV0LgoKIyMjIyBIb3cgd2lsbCBJIGJlIGxlYXJuaW5nIFI/IApJbiB0aGlzIGNvdXJzZSwgeW91IHdpbGwgYmUgdXNpbmcgYW5kIGxlYXJuaW5nIFIgd2l0aGluIHRoZSBSU3R1ZGlvIGVudmlyb25tZW50LiBSU3R1ZGlvIGlzIGFuIEludGVncmF0ZWQgRGV2ZWxvcG1lbnQgRW52aXJvbm1lbnQgKGEgZ3JhcGhpY2FsIHVzZXIgaW50ZXJmYWNlIGZvciBSIGRldmVsb3BtZW50LikgU29tZSBEYXRhIEFuYWx5c3QgYmVsaWV2ZXMgdGhhdCBSU3R1ZGlvIG1ha2VzIGxpZmUgZWFzaWVyIGZvciB3b3JraW5nIHdpdGggUiwgYnV0IHlvdSBjYW4gYWxzbyBydW4gUiB0aHJvdWdoIGEgQ29tbWFuZCBMaW5lIEludGVyZmFjZS4KClIgYW5kIFJTdHVkaW8gYXJlIGJvdGggb3Blbi1zb3VyY2Ugc29mdHdhcmUsIHdoaWNoIG1lYW5zIGVhY2ggcGllY2Ugb2Ygc29mdHdhcmUgaXMgZnJlZSBhbmQgaGFzIGFjdGl2ZSB1c2VyIGFuZCBkZXZlbG9wbWVudCBjb21tdW5pdGllcy4gQmVjYXVzZSBvZiB0aGVzZSBjb21tdW5pdGllcywgYSBsb3Qgb2YgcmVzb3VyY2VzIGhhdmUgYmVlbiBjcmVhdGVkIGJ5IGZlbGxvdyBSIHByb2dyYW1tZXJzLiBTb21lIGV4YW1wbGVzIGFyZSBTdGFjay1FeGNoYW5nZSwgU3RhY2sgT3ZlcmZsb3csIGV0Yy4KCk9uY2UgeW91J3ZlIGluc3RhbGxlZCBSIGFuZCBSU3R1ZGlvIHlvdSB3aWxsIGJlIHJlYWR5IHRvIG1vdmUgb24gdG8gdGhlIG5leHQgdGFzay4KCiMjIyMgSG93IGRvIEkgZG93bmxvYWQgUiBhbmQgUlN0dWRpbz8KSGVyZSBpcyB0aGUgd2Vic2l0ZSB3aGVyZSB5b3UgZG93bmxvYWQgUi4KaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvCgpIb3cgZG8gSSBpbnN0YWxsIFJTdHVkaW8/IEhlcmUgaXMgd2hlcmUgeW91IGNhbiBkb3dubG9hZCBSU3R1ZGlvLgpodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9wcm9kdWN0cy9yc3R1ZGlvL2Rvd25sb2FkLwoKIyMjIDIuIEdldCB0byBLbm93IFIgU3R1ZGlvCldpdGhpbiB0aGlzIHNlY3Rpb24gb2YgdGhlIHR1dG9yaWFsLCB5b3Ugd2lsbCBsZWFybiBSU3R1ZGlvJ3MgZ2VuZXJhbCBsYXlvdXQgYW5kIHRoZSBmdW5jdGlvbmFsaXR5IHRoYXQgZWFjaCBwb3J0aW9uIG9mIHRoZSBpbnRlcmZhY2UgcHJvdmlkZXMuCgojIyMjIEJlY29taW5nIEZhbWlsaWFyIHdpdGggUlN0dWRpbwpCZWZvcmUgeW91IGJlZ2luIGxlYXJuaW5nIFIsIGxldCdzIG9wZW4gUlN0dWRpbyB3aXRoaW4geW91ciBjb21wdXRlciB0byB0YWtlIGEgbG9vayBhdCBob3cgdG8gd29yayBSU3R1ZGlvLiBBZnRlciBvcGVuaW5nIHRoZSBwcm9ncmFtIHlvdSB3aWxsIHNlZSBmb3VyIHNlY3Rpb25zIHdpdGhpbiB0aGUgaW50ZXJmYWNlLgohW10oaW1hZ2VzLzMucG5nKQoKVGhlIDFzdCBTZWN0aW9uIChVcHBlciBsZWZ0IGJveCkgaXMgdGhlIFIgU2NyaXB0IGJveC4gVGhlIFIgU2NyaXB0IGJveCBpcyAxIG9mIHRoZSAyIHBsYWNlcyB0aGF0IHlvdSBjYW4gdHlwZSBpbiB5b3VyIGNvZGUuIFRoZSBhZHZhbnRhZ2VzIG9mIHRoZSBSIFNjcmlwdCBib3ggYXJlIHRoYXQgeW91IGNhbiBlZGl0IHlvdXIgY29kZSwgc2F2ZSB5b3VyIGNvZGUgKGJ5IGNsaWNraW5nIHRoZSBibHVlIGZsb3BweSBkaXNrIGljb24pLCBydW4geW91ciBjb2RlLCBhbmQgeW91IGNhbiBoYXZlIHNldmVyYWwgdGFicyBvcGVuIHNpbXVsdGFuZW91c2x5LiAKIVtdKGltYWdlcy80LnBuZykKCkluIGFkZGl0aW9uIHRvIHR5cGluZyB5b3VyIGNvZGUsIHlvdSBjYW4gYWxzbyB2aWV3IHRoZSBkYXRhIHlvdeKAmXJlIHdvcmtpbmcgd2l0aC4gRm9yIGV4YW1wbGU6CiFbXShpbWFnZXMvNS5wbmcpCgpUaGUgMm5kIHNlY3Rpb24gKGxvd2VyIGxlZnQpIGlzIGNhbGxlZCB0aGUgQ29uc29sZS4gVGhlIENvbnNvbGUgaXMgdGhlIDJuZCBwbGFjZSB5b3UgY2FuIHR5cGUgYW5kIHJ1biB5b3VyIGNvZGUuIEl0IGlzIGFsc28gd2hlcmUgeW914oCZbGwgc2VlIHdoaWNoIFIgdmVyc2lvbiB5b3UgYXJlIHJ1bm5pbmcgKGluIHRoZSByZWQgYm94KSwgd2hpY2ggZm9sZGVyIHlvdeKAmXJlIHdvcmtpbmcgaW4gb24geW91ciBjb21wdXRlciAoaW4gYmx1ZSksIGFuZCB3aGVyZSB5b3XigJlsbCBzZWUgdGhlIG91dHB1dCBvZiB5b3VyIGNvZGUuCiFbXShpbWFnZXMvNi5wbmcpCgpUaGUgM3JkIHNlY3Rpb24gKHVwcGVyIHJpZ2h0IGJveCkgY29udGFpbnMgdGhlIEVudmlyb25tZW50IHRhYiBhbmQgdGhlIEhpc3RvcnkgdGFiLiBUaGUgSGlzdG9yeSB0YWIgc3RvcmVzIGFsbCBjb2RlIHRoYXQgeW914oCZdmUgY3JlYXRlZCBkdXJpbmcgeW91ciBzZXNzaW9uLiAoSXQgZG9lcyBub3Qgc3RvcmUgb3V0cHV0cy4pIFdpdGhpbiB5b3VyIEVudmlyb25tZW50IHRhYiB5b3UgY2FuIHVwbG9hZCB5b3VyIGRhdGFzZXQgYW5kIHNhdmUgeW91ciBlbnZpcm9ubWVudC4KIVtdKGltYWdlcy83LnBuZykKCkluIHRoZSA0dGggc2VjdGlvbiAoYm90dG9tIHJpZ2h0IGJveCksIHRoZXJlIGFyZSBmaXZlIHRhYnMuIFRoZSBmaXJzdCB0YWIsIEZpbGVzLCBkaXNwbGF5cyB0aGUgZmlsZXMgYW5kIGZvbGRlcnMgd2l0aGluIHlvdXIgY29tcHV0ZXIvUlN0dWRpby4gVGhlIHNlY29uZCB0YWIsIFBsb3RzLCBpcyB3aGVyZSB5b3XigJlsbCBiZSBhYmxlIHRvIHNlZSwgc2F2ZSwgYW5kIGV4cG9ydCB0aGUgcGxvdHMgeW91ciBwcm9kdWNlLiBUaGUgdGhpcmQgdGFiLCBQYWNrYWdlcywgc2hvd3MgdGhlIHBhY2thZ2VzIHRoYXQgeW914oCZdmUgZG93bmxvYWRlZCBvciB0aGF0IGFyZSBhdmFpbGFibGUgdG8gZG93bmxvYWQuIFRoZSBmb3VydGggdGFiLCBIZWxwLCBpcyB3aGVyZSB5b3UgY2FuIGdvIHRvIHJlY2VpdmUgYWRkaXRpb25hbCBkb2N1bWVudGF0aW9uIGNvbmNlcm5pbmcgY2VydGFpbiB0b3BpY3MgYW5kIHBhY2thZ2VzLiBUaGUgZmlmdGggdGFiLCBWaWV3ZXIsIGlzIHVzZWQgdG8gZm9yIHZpZXdpbmcgd2ViIGNvbnRlbnQuIChXZSB3b27igJl0IGJlIHVzaW5nIG11Y2ggb2YgdGhpcyB0YWIgZHVyaW5nIHRoaXMgdHV0b3JpYWwuKQoKWW91J3JlIG5vdyByZWFkeSB0byBtb3ZlIG9uIHRvIHRoZSBuZXh0IHRhc2sgdG8gYmVnaW4gbGVhcm5pbmcgUi4gSWYgeW914oCZZCBsaWtlIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gb24gdGhlIFJTdHVkaW8gSURFLCBoZXJlIGlzIGEgaGVscGZ1bCBjaGVhdCBzaGVldC4KaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTYvMDEvcnN0dWRpby1JREUtY2hlYXRzaGVldC5wZGYKIVtdKGltYWdlcy9jaGVhdF9zaGVldF9wZzIucG5nKQoKIyMjIDMuIFdhbGsgVGhyb3VnaCB0aGUgUiBUdXRvcmlhbCAtIFBhcnQgMQpXaXRoaW4gdGhpcyBzZWN0aW9uIG9mIHRoZSB0dXRvcmlhbCwgeW91IHdpbGwgbGVhcm4gaG93IHRvIHN0YXJ0IGEgbmV3IFIgU3R1ZGlvIHByb2plY3QsIGluc3RhbGwgcGFja2FnZXMsIGNhbGwgb24gYSBwYWNrYWdlIChsaWJyYXJ5KSBhbmQgdXBsb2FkIGEgZGF0YXNldCBpbnRvIFJTdHVkaW8uCgpSZWFkaW5nIHRoZSBXb3JraW5nIHdpdGggUHJvamVjdHMgaW4gUlN0dWRpbyBzZWN0aW9uIG9mIHRoZSBSZXNvdXJjZXMgd2lsbCBoZWxwIHdpdGggdGhlIG5leHQgc3RlcHMuCgojIyMjIFN0YXJ0aW5nIGEgbmV3IHByb2plY3QKUiBTdHVkaW8gaGFzIGEgc3lzdGVtIGZvciBvcmdhbml6aW5nIHlvdXIgcHJvamVjdHMgYW5kIGtlZXBpbmcgYWxsIG9mIHlvdXIgd29yayB3aXRoaW4gaXRzIG93biB3b3JraW5nIGRpcmVjdG9yeSwgd29ya3NwYWNlLCBoaXN0b3J5LCBhbmQgc291cmNlIGRvY3VtZW50cy4gSW4gb3JkZXIgdG8gd29yayBlZmZlY3RpdmVseSBhbmQgZWZmaWNpZW50bHkgaW4gUiBTdHVkaW8gaXQgaXMgaW1wb3J0YW50IHRoYXQgeW91IGFsd2F5cyBzdGFydCBhIG5ldyBSIFN0dWRpbyBwcm9qZWN0IHdoZW5ldmVyIHlvdSBzdGFydCBhIG5ldyB0YXNrLiBUaGlzIHdpbGwga2VlcCB5b3VyIHdvcmsgc2VwYXJhdGUgYW5kIG1ha2UgdGhlIHdvcmsgcG9ydGFibGUgYW5kIGVhc3kgdG8gc2hhcmUgd2l0aCB5b3VyIGNsYXNzbWF0ZXMgYW5kIHlvdXIgbWVudG9yLiBIZXJlIGFyZSB0aGUgc3RlcHMgeW91IHNob3VsZCBhbHdheXMgZm9sbG93IHdoZW4gc3RhcnRpbmcgYSBuZXcgcHJvamVjdCBpbiBSIFN0dWRpbzoKClJTdHVkaW8gcHJvamVjdHMgYXJlIGFzc29jaWF0ZWQgd2l0aCBSIHdvcmtpbmcgZGlyZWN0b3JpZXMuIFlvdSBjYW4gY3JlYXRlIGFuIFJTdHVkaW8gcHJvamVjdDoKCgoqIEluIGEgYnJhbmQgbmV3IGRpcmVjdG9yeQoqIEluIGFuIGV4aXN0aW5nIGRpcmVjdG9yeSB3aGVyZSB5b3UgYWxyZWFkeSBoYXZlIFIgY29kZSBhbmQgZGF0YQoqIEJ5IGNsb25pbmcgYSB2ZXJzaW9uIGNvbnRyb2wgKEdpdCBvciBTdWJ2ZXJzaW9uKSByZXBvc2l0b3J5ClRvIGNyZWF0ZSBhIG5ldyBwcm9qZWN0IHVzZSB0aGUgTmV3IFByb2plY3QgY29tbWFuZCAoYXZhaWxhYmxlIG9uIHRoZSBSU3R1ZGlvIEZpbGUgbWVudSBhbmQgb24gdGhlIGdsb2JhbCB0b29sYmFyKToKCldoZW4gYSBuZXcgcHJvamVjdCBpcyBjcmVhdGVkIFJTdHVkaW8gdGhlIGZvbGxvd2luZyB0YWtlcyBwbGFjZToKCjEuIENyZWF0ZXMgYSBwcm9qZWN0IGZpbGUgKHdpdGggYW4gLlJwcm9qIGV4dGVuc2lvbikgd2l0aGluIHRoZSBwcm9qZWN0IGRpcmVjdG9yeS4gVGhpcyBmaWxlIGNvbnRhaW5zIHZhcmlvdXMgcHJvamVjdCBvcHRpb25zIChkaXNjdXNzZWQgYmVsb3cpIGFuZCBjYW4gYWxzbyBiZSB1c2VkIGFzIGEgc2hvcnRjdXQgZm9yIG9wZW5pbmcgdGhlIHByb2plY3QgZGlyZWN0bHkgZnJvbSB0aGUgZmlsZXN5c3RlbS4KMi4gQ3JlYXRlcyBhIGhpZGRlbiBkaXJlY3RvcnkgKG5hbWVkIC5ScHJvai51c2VyKSB3aGVyZSBwcm9qZWN0LXNwZWNpZmljIHRlbXBvcmFyeSBmaWxlcyAoZS5nLiBhdXRvLXNhdmVkIHNvdXJjZSBkb2N1bWVudHMsIHdpbmRvdy1zdGF0ZSwgZXRjLikgYXJlIHN0b3JlZC4gVGhpcyBkaXJlY3RvcnkgaXMgYWxzbyBhdXRvbWF0aWNhbGx5IGFkZGVkIHRvIC5SYnVpbGRpZ25vcmUsIC5naXRpZ25vcmUsIGV0Yy4gaWYgcmVxdWlyZWQuCjMuIExvYWRzIHRoZSBwcm9qZWN0IGludG8gUlN0dWRpbyBhbmQgZGlzcGxheSBpdHMgbmFtZSBpbiB0aGUgUHJvamVjdHMgdG9vbGJhciAod2hpY2ggaXMgbG9jYXRlZCBvbiB0aGUgZmFyIHJpZ2h0IHNpZGUgb2YgdGhlIG1haW4gdG9vbGJhcikKCiMjIyMgSW5zdGFsbGluZyBQYWNrYWdlcwpIb3cgdG8gaW5zdGFsbCBwYWNrYWdlcyBpbiBSU3R1ZGlvOiBUaGUgcGFja2FnZSB0aGF0IHlvdSB3aWxsIG5lZWQgZm9yIHRoaXMgcGFydCBvZiB0aGUgdHV0b3JpYWwgaXMgdGhlIHJlYWRyIHBhY2thZ2UuIFRoZSByZWFkciBwYWNrYWdlIGlzIGRlc2lnbmVkIHRvIHJlYWQgZmlsZXMsIGxpa2UgY3N2IGZpbGVzLiAoQWx0aG91Z2ggd2Ugd2lsbCBvbmx5IGJlIGdvaW5nIG92ZXIgb25lIGFsdGVybmF0aXZlLCB0aGVyZSBhcmUgbXVsdGlwbGUgd2F5cyB0byBpbnN0YWxsIGEgcGFja2FnZS4pCgpZb3Ugd2lsbCBiZSB1c2luZyB0aGUgaW5zdGFsbC5wYWNrYWdlcygpIGZ1bmN0aW9uIHRvIGluc3RhbGwgdGhlIHJlYWRyIHBhY2thZ2UuCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCdyZWFkcicpCmBgYApUSVA6IElmIHlvdSBhcmUgdHlwaW5nIGluIHRoZSBSIFNjcmlwdCBwYW5lLCBwcmVzcyBDT01NQU5EICsgRU5URVIgdG8gcnVuIHlvdXIgY29kZS4KCiMjIyMgQ2FsbGluZyBvbiBhIFBhY2thZ2UKQWZ0ZXIgdGhlIGluc3RhbGxhdGlvbiBvZiB0aGUgcGFja2FnZSwgeW91IG5lZWQgdG8gY2FsbCBvbiBpdCB0byBoYXZlIGFjY2VzcyB0byBpdHMgZnVuY3Rpb25zLiBUaGVyZSBhcmUgbXVsdGlwbGUgd2F5cyB0byBjYWxsIGEgbGlicmFyeS4gVG8gc2VlIG9uZSBvZiB0aGVtLCB0eXBlIHRoaXMgY29kZSBsaW5lIGludG8geW91ciBSIFNjcmlwdCBPUiBDb25zb2xlIHNlY3Rpb24uCmBgYHtyfQpsaWJyYXJ5KHJlYWRyKSAKYGBgCgojIyMjIFVwbG9hZGluZyBZb3VyIERhdGEKTm93IGl04oCZcyB0aW1lIHRvIHVwbG9hZCB0aGUgZGF0YXNldCB0aGF0IHlvdeKAmWxsIGJlIHVzaW5nIGR1cmluZyB0aGlzIHR1dG9yaWFsICh0aGUgLkNTViBmaWxlKSBpbnRvIFJTdHVkaW8uICBXaGVuIHlvdSBwZXJmb3JtIHRoaXMgYWN0aW9uLCB5b3UgYXJlIGNyZWF0aW5nIGEgZGF0YSBmcmFtZSwgd2hpY2ggbWVhbnMgdGhhdCBhbGwgb2YgdGhlIGRhdGEgdGhhdCBpcyBzdG9yZWQgd2l0aGluIHRoZSBkYXRhIGZyYW1lIGlzIHNlcGFyYXRlZCBieSBjb2x1bW5zLiBMaWtlIHRoZSBwcmV2aW91cyBzdGVwcywgdGhlcmUgYXJlIG11bHRpcGxlIHdheXMgdG8gdXBsb2FkIGEgZGF0YXNldC4gSGVyZSBpcyBvbmUgb3B0aW9uLgoKVElQOgpZb3VyIGRhdGFzZXQgaXMgY2Fycy5jc3YsIHdoaWNoIGlzIGxvY2F0ZWQgd2l0aGluIHRoZSB6aXAgZmlsZSBhdHRhY2hlZCB0byBEYW5pZWxsZSdzIGVtYWlsLCBidXQgeW91J2xsIHdhbnQgdG8gY3JlYXRlIGEgbmFtZSBmb3IgeW91ciBkYXRhc2V0LiBZb3Ugd2lsbCBhbHNvIG5lZWQgdG8gbWFrZSBzdXJlIHRoYXQgeW91IHJlYWQgaG93IHRvIHNldCB1cCB5b3VyIHdvcmtpbmcgZW52aXJvbm1lbnQgb3IgdGhpcyBsaW5lIG9mIGNvZGUgd2lsbCBub3Qgd29yay4gCmBgYHtyfQpkZl9jYXJzPC0gcmVhZF9jc3YoIlIgVHV0b3JpYWwgRGF0YSBTZXRzL2NhcnMuY3N2Iik7ClZpZXcoZGZfY2FycykKcHJpbnQoZGZfY2FycykKCgpkZl9pcmlzPC0gcmVhZF9jc3YoIlIgVHV0b3JpYWwgRGF0YSBTZXRzL2lyaXMuY3N2IikKZGZfaXJpcyA8LSBzdWJzZXQoZGZfaXJpcywgc2VsZWN0ID0gLWMoWDEpKQpWaWV3KGRmX2lyaXMpCnByaW50KGRmX2lyaXMpCmBgYAoKIyMjIDQuIFdhbGsgVGhyb3VnaCB0aGUgUiBUdXRvcmlhbCAtIFBhcnQgMgpXaXRoaW4gdGhpcyBzZWN0aW9uIG9mIHRoZSB0dXRvcmlhbCwgeW91IHdpbGwgbGVhcm4gaG93IHRvIGdldCB0byBrbm93IHlvdXIgZGF0YSwgcHJlcHJvY2VzcyB5b3VyIGRhdGEsIGFuZCBjcmVhdGUgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cyB3aXRoaW4gUlN0dWRpby4KCiMjIyMgR2V0dGluZyB0byBLbm93IFlvdXIgRGF0YQpPbmNlIHlvdeKAmXZlIHVwbG9hZGVkIHlvdXIgZGF0YXNldCwgaXTigJlzIGFsd2F5cyBhIGdvb2QgaWRlYSB0byBnZXQgdG8ga25vdyB5b3VyIGRhdGEuIEhlcmUgYXJlIHNvbWUgaGVscGZ1bCBmdW5jdGlvbnMgdGhhdCB3aWxsIGhlbHAgeW91IGdldCBhY3F1YWludGVkIHdpdGggeW91ciBkYXRhOgoKYGBge3J9CkdldF90b19rbm93X2RhdGEgPC0gZnVuY3Rpb24oRGF0YXNldE5hbWUpewogIHByaW50KHBhc3RlKCJHRVQgVE8gS05PVzogIixkZXBhcnNlKHN1YnN0aXR1dGUoRGF0YXNldE5hbWUpKSkpCiAgCiAgcHJpbnQoJ2ZldGNoaW5nIGF0dHJpYnV0ZXMuLi4nKQogIHByaW50KGF0dHJpYnV0ZXMoRGF0YXNldE5hbWUpICkjTGlzdCB5b3VyIGF0dHJpYnV0ZXMgd2l0aGluIHlvdXIgZGF0YSBzZXQuCiAgCiAgcHJpbnQoJ2ZldGNoaW5nIHN1bW1hcnkuLi4nKQogIHByaW50KHN1bW1hcnkoRGF0YXNldE5hbWUpICkjUHJpbnRzIHRoZSBtaW4sIG1heCwgbWVhbiwgbWVkaWFuLCBhbmQgcXVhcnRpbGVzIG9mIGVhY2ggYXR0cmlidXRlLgogIAogIHByaW50KCdmZXRjaGluZyBkYXRhIHN0cnVjdHVyZS4uLicpCiAgcHJpbnQoc3RyKERhdGFzZXROYW1lKSApI0Rpc3BsYXlzIHRoZSBzdHJ1Y3R1cmUgb2YgeW91ciBkYXRhIHNldC4KICAKICBwcmludCgnZmV0Y2hpbmcgZGF0YSBhdHRyaWJ1dGUgbmFtZXMuLi4nKQogIHByaW50KG5hbWVzKERhdGFzZXROYW1lKSApI05hbWVzIHlvdXIgYXR0cmlidXRlcyB3aXRoaW4geW91ciBkYXRhIHNldC4KCiAgI0RhdGFzZXROYW1lJENvbHVtbk5hbWUgI1dpbGwgcHJpbnQgb3V0IHRoZSBpbnN0YW5jZXMgd2l0aGluIHRoYXQgcGFydGljdWxhciBjb2x1bW4gaW4geW91ciBkYXRhIHNldC4KfQoKR2V0X3RvX2tub3dfZGF0YShkZl9jYXJzKQpgYGAKYGBge3J9CkdldF90b19rbm93X2RhdGEoZGZfaXJpcykKYGBgCgpQbG90dGluZyBpcyBhbHNvIGEgaGVscGZ1bCB3YXkgdG8gdmlldyB5b3VyIGRhdGFzZXQuIEhlcmUgYXJlIHNvbWUgaGVscGZ1bCBmdW5jdGlvbnMgdGhhdCB3aWxsIGhlbHAgeW91IGdldCBhY3F1YWludGVkIHdpdGggeW91ciBkYXRhIHZpc3VhbGx5OgoKVElQOgpZb3VyIGNvbHVtbnMgbXVzdCBiZSBpbiBudW1lcmljIGZvcm0gdG8gcGVyZm9ybSB0aGVzZSBwbG90cy4KYGBge3J9CnBsb3Rfc3VtbWFyeV9vZl9kYXRhPC1mdW5jdGlvbihEYXRhc2V0TmFtZSx4X2luZGV4PTEpewogIAogIGNvbHVtbl9uYW1lcyA9IG5hbWVzKERhdGFzZXROYW1lKQogIAogIHN1YnBsb3RfY29scyA9IDIKICBzdWJwbG90X3Jvd3MgPSAyCiAgcGFyKG1mcm93PWMoc3VicGxvdF9yb3dzLHN1YnBsb3RfY29scykpICAKICAKICB4IDwtIHVubGlzdChEYXRhc2V0TmFtZVsseF9pbmRleF0pCiAgeF9oZWFkZXIgPSBjb2x1bW5fbmFtZXNbeF9pbmRleF0KICAKICBmb3IoaSBpbiAxOmxlbmd0aChjb2x1bW5fbmFtZXMpKXsKICAgIHkgPC0gdW5saXN0KERhdGFzZXROYW1lWyxpXSkKICAgIHlfaGVhZGVyID0gY29sdW1uX25hbWVzW2ldCiAgICB0cnkoaGlzdCh5LCBtYWluID0gcGFzdGUoeV9oZWFkZXIsICJIaXN0b2dyYW0iKSwgeGxhYiA9IHlfaGVhZGVyICksc2lsZW50PVRSVUUpI0hpc3RvZ3JhbSBQbG90CiAgfQogIAogIGZvcihpIGluIDE6bGVuZ3RoKGNvbHVtbl9uYW1lcykpewogICAgCiAgICBpZihpICE9IHhfaW5kZXgpIHsKICAgIHkgPC0gdW5saXN0KERhdGFzZXROYW1lWyxpXSkKICAgIHlfaGVhZGVyID0gY29sdW1uX25hbWVzW2ldCiAgICAKICAgIHRyeShwbG90KHgseSwgeGxhYiA9IHhfaGVhZGVyLCB5bGFiID0geV9oZWFkZXIpLHNpbGVudD1UUlVFKSAgI1NjYXR0ZXIgKEJveCkgUGxvdAogICAgfSAKICB9CiAgCiAgI05vcm1hbCBRdWFudGlsZSBQbG90LSBpcyBhIHdheSB0byBzZWUgaWYgeW91ciBkYXRhIGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLgogIGZvcihpIGluIDE6bGVuZ3RoKGNvbHVtbl9uYW1lcykpewogICAgCiAgICB5IDwtIHVubGlzdChEYXRhc2V0TmFtZVssaV0pCiAgICB5X2hlYWRlciA9IGNvbHVtbl9uYW1lc1tpXQogICAgCiB0cnkocXFub3JtKHksbWFpbiA9IHBhc3RlKHlfaGVhZGVyLCAiIE5vcm1hbCBRLVEgUGxvdCIpKSxzaWxlbnQ9VFJVRSkgIyNOb3JtYWwgUXVhbnRpbGUgUGxvdAogIH0KfQoKcGxvdF9zdW1tYXJ5X29mX2RhdGEoZGZfY2Fycyx4X2luZGV4PTIpCmBgYAoKYGBge3J9CnBsb3Rfc3VtbWFyeV9vZl9kYXRhKGRmX2lyaXMseF9pbmRleD0xKQpgYGAKCgojIyMjIFByZXByb2Nlc3NpbmcgeW91ciBEYXRhClByZXByb2Nlc3NpbmcsIGFsc28ga25vd24gYXMgZGF0YSBjbGVhbmluZywgaXMgYSB2aXRhbCBzdGVwIGluIHlvdXIgYW5hbHlzaXMgcHJvY2Vzcy4gU29tZSByZWFzb25zIHRvIHByZXBhcmUgeW91ciBkYXRhIGlzIHNvIGl0IGNhbiBiZSBhbmFseXplZCwgaXQgbWlnaHQgYmUgbm9pc3kgZGF0YSAobWlzc2luZyB2YWx1ZXMvb3V0bGllcnMpLCBpdCBjb3VsZCBoYXZlIGF0dHJpYnV0ZXMgdGhhdCBhcmVu4oCZdCBoZWxwZnVsLCBldGMuIFRoZXJlIGFyZSBtYW55IHN0ZXBzIHRoYXQgb25lIG1heSB0YWtlIHdoZW4gcHJlcGFyaW5nIHlvdXIgZGF0YSwgd2Ugd2lsbCBvbmx5IGJlIGRpc2N1c3NpbmcgYSBoYW5kZnVsIGF0IGEgaGlnaC1sZXZlbCB2aWV3LgoKV2hlbiB3b3JraW5nIHdpdGggZGF0YSwgeW91IHdpbGwgbmVlZCB0byBiZSBhd2FyZSBvZiB3aGF0IGEgdmVjdG9yIGlzLiBBIHZlY3RvciBpcyBhIHNlcXVlbmNlIG9mIHRoZSBzYW1lIGRhdGEgdHlwZS4KCkhlcmUgYXJlIHRoZSBkYXRhIHR5cGVzIHRoYXQgeW91IHdpbGwgZW5jb3VudGVyIHdoZW4gd29ya2luZyBpbiBSOgoKKiBOdW1lcmljLSBOdW1iZXJzIHdpdGggZGVjaW1hbHMuIChFeDogMS4wLCAxMC41LCA0LjUsIGV0Yy4pCiogSW50ZWdlciBEYXRhLSBXaG9sZSBudW1iZXJzIChFeDogMTEsIDQ1LCA3OCwgZXRjLikKKiBGYWN0b3IgRGF0YS0gQ2F0ZWdvcmljYWwgZGF0YSAoRXg6IFJlZCwgQmx1ZSwgR3JlZW4sIFB1cnBsZSwgZXRjLikKKiBPcmRpbmFsIERhdGEtIE9yZGVyZWQgZGF0YSAoRXg6IGVkdWNhdGlvbmFsIGxldmVscywgdGVtcGVyYXR1cmUsIGV0Yy4pCiogQ2hhcmFjdGVyIERhdGEtIFN0cmluZyB2YWx1ZXMsIHdoaWNoIGFyZSBjaGFyYWN0ZXJzICh3b3Jkcykgd2l0aCBxdW90ZXMgYXJvdW5kIHRoZW0uIChFeDog4oCcUmVk4oCdLCDigJxCbHVl4oCdLCDigJxHcmVlbuKAnSwg4oCcUHVycGxl4oCdLCBldGMuKQoqIExvZ2ljYWwtIFRSVUUgb3IgVFJVRSAoQWx3YXlzIGNhcGl0YWxpemUgVFJVRSBvciBGQUxTRSkKCkRvIHlvdSBzZWUgYW55IGRhdGEgdHlwZXMgdGhhdCBuZWVkIGNoYW5naW5nIHdpdGhpbiB5b3VyIGRhdGEgc2V0PyBJZiBzbywgaG93IGRvIHlvdSBjb252ZXJ0IGRhdGEgdHlwZXM/IENvbnZlcnRpbmcgZGF0YSB0eXBlcyBpcyBhIGhlbHBmdWwgc2tpbGwgdG8gbGVhcm4gZm9yIHRoaXMgdHV0b3JpYWwgYW5kIGZ1dHVyZSBhbmFseXNlcy4gSGVyZSBpcyBhbiBleGFtcGxlIG9mIGhvdyBvbmUgd291bGQgY2hhbmdlIGEgY29sdW1u4oCZcyBkYXRhIHR5cGUgd2l0aGluIGEgZGF0YSBzZXQ6CmBgYHtyfQojRGF0YXNldE5hbWUkQ29sdW1uTmFtZTwtYXMudHlwZW9mZGF0YShEYXRhc2V0TmFtZSRDb2x1bW5OYW1lKQpgYGAKCkRvIHRoZSBjb2x1bW5zL2F0dHJpYnV0ZXMgd2l0aGluIHlvdXIgZGF0YXNldCBuZWVkIHJlbmFtaW5nPwoKPiA8c3BhbiBzdHlsZT0nY29sb3I6cmVkOyBmb250LXNpemU6MTRweCc+IHRoZSBudW1lcmljIGRhdGEgc2VlbXMgZmluZSAoYWxsICdkYmwnKSwgaG93ZXZlciB0aGUgY2hhcmFjdGVyIGRhdGEgc2hvdWxkIGJlIGNoYW5nZWQgdG8gJ0ZhY3RvcicgZGF0YSBhcyBpdCBpcyBhbGwgY2F0ZWdvcmljYWwgPC9zcGFuPgoKYGBge3J9CmRmX2NhcnMkIm5hbWUgb2YgY2FyIjwtYXMuZmFjdG9yKGRmX2NhcnMkIm5hbWUgb2YgY2FyIikKZGZfaXJpcyQiU3BlY2llcyI8LWFzLmZhY3RvcihkZl9pcmlzJCJTcGVjaWVzIikKYGBgCgpUbyByZW5hbWUgdGhlIGF0dHJpYnV0ZXMvY29sdW1ucyBpbiB5b3VyIGRhdGFzZXQsIHlvdSdsbCB3YW50IHRvIHVzZSB0aGUgYygpIGZ1bmN0aW9uLCBzcGVjaWZ5aW5nIGEgbmFtZSBmb3IgZWFjaCBjb2x1bW4uIAoKYGBge3J9CnJlZm9ybWF0X2NvbHVtbl9oZWFkZXJzIDwtIGZ1bmN0aW9uKERhdGFzZXROYW1lKXsKICBjb2x1bW5fbmFtZXMgPSBuYW1lcyhEYXRhc2V0TmFtZSkKICAKICBjb2x1bW5fbmFtZXNfcmVmb3JtYXRlZCA9IGdzdWIoIiAiLCJfIixmaXhlZD1UUlVFLGNvbHVtbl9uYW1lcykKICAKICBuYW1lcyhEYXRhc2V0TmFtZSk8LWMoY29sdW1uX25hbWVzX3JlZm9ybWF0ZWQpCiAgCiAgcmV0dXJuKERhdGFzZXROYW1lKQp9CgpkZl9jYXJzID0gcmVmb3JtYXRfY29sdW1uX2hlYWRlcnMoZGZfY2FycykKcHJpbnQobmFtZXMoZGZfY2FycykpCmBgYApgYGB7cn0KZGZfaXJpcyA9IHJlZm9ybWF0X2NvbHVtbl9oZWFkZXJzKGRmX2lyaXMpCnByaW50KG5hbWVzKGRmX2lyaXMpKQpgYGAKCkRvIGFueSBvZiB5b3VyIHZhcmlhYmxlcyBoYXZlIG1pc3NpbmcgdmFsdWVzPyBIb3cgZG8geW91IGtub3cgaWYgeW91ciBkYXRhc2V0IGhhcyBhbnkgbWlzc2luZyB2YWx1ZXM/IElmIHlvdSBkbyBub3QgYWRkcmVzcyBtaXNzaW5nIHZhbHVlcyBjZXJ0YWluIGZ1bmN0aW9ucyB3aWxsIG5vdCB3b3JrIHByb3Blcmx5LCBzbyBpdOKAmXMgc21hcnQgdG8gc3RhcnQgdGhlIHByYWN0aWNlIGNoZWNraW5nIGZvciBtaXNzaW5nIHZhbHVlcy4gIFIgbGFiZWxzIG1pc3NpbmcgYXMgTkEgKE5vdCBBdmFpbGFibGUpLiBIZXJlIGFyZSB0d28gd2F5cyB0byBrbm93IGlmIHlvdSBoYXZlIGFueSBtaXNzaW5nIHZhbHVlczoKYGBge3J9CnN1bW1hcnkoZGZfY2FycykgI1dpbGwgY291bnQgaG93IG1hbnkgTkHigJlzIHlvdSBoYXZlLgpgYGAKCmBgYHtyfQpzdW1tYXJ5KGRmX2lyaXMpCmBgYAoKYGBge3J9CmlzLm5hKGRmX2NhcnMpICNXaWxsIHNob3cgeW91ciBOQeKAmXMgdGhyb3VnaCBsb2dpY2FsIGRhdGEuIChUUlVFIGlmIGl04oCZcyBtaXNzaW5nLCBGQUxTRSBpZiBpdOKAmXMgbm90LikKYGBgCgpIb3cgdG8gYWRkcmVzcyBtaXNzaW5nIHZhbHVlcz8gVGhlcmUgYXJlIG11bHRpcGxlIHdheXMgdG8gY29uZnJvbnQgbWlzc2luZyB2YWx1ZXMgaW4geW91ciBkYXRhc2V0IOKAkyBhbGwgZGVwZW5kIG9uIGhvdyBtdWNoIHRoZXkgd2lsbCBhZmZlY3QgeW91ciBkYXRhc2V0LiBIZXJlIGFyZSBhIGZldyBvcHRpb25zOgoKKiBSZW1vdmUgYW55IG9ic2VydmF0aW9ucyBjb250YWluaW5nIG1pc3NpbmcgZGF0YS4gKElmIHRoZSBtaXNzaW5nIGRhdGEgaXMgbGVzcyB0aGFuIDEwJSBvZiB0aGUgdG90YWwgZGF0YSBhbmQgb25seSBhZnRlciBjb21wYXJpbmcgdGhlIG1pbi9tYXggb2YgYWxsIHRoZSBmZWF0dXJlcyBib3RoIHdpdGggYW5kIHdpdGhvdXQgdGhlIG1pc3NpbmcgZGF0YS4pCgpgYGB7cn0KbmEub21pdChkZl9jYXJzKSNEcm9wcyBhbnkgcm93cyB3aXRoIG1pc3NpbmcgdmFsdWVzIGFuZCBvbWl0cyB0aGVtIGZvcmV2ZXIuCmBgYApgYGB7cn0KbmEub21pdChkZl9pcmlzKQpgYGAKCmBgYHtyfQojbmEuZXhjbHVkZShEYXRhc2V0TmFtZSRDb2x1bW5OYW1lKSNEcm9wcyBhbnkgcm93cyB3aXRoIG1pc3NpbmcgdmFsdWVzLCBidXQga2VlcHMgdHJhY2sgb2Ygd2hlcmUgdGhleSB3ZXJlLgpgYGAKClJlcGxhY2UgdGhlIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIG1lYW4sIHdoaWNoIGlzIGNvbW1vbiB0ZWNobmlxdWUsIGJ1dCBzb21ldGhpbmcgdG8gdXNlIHdpdGggY2FyZSB3aXRoIGFzIGl0IGNhbiBza2V3IHRoZSBkYXRhLgoKYGBge3J9CiNEYXRhc2V0TmFtZSRDb2x1bW5OYW1lW2lzLm5hKERhdGFzZXROYW1lJENvbHVtbk5hbWUpXTwtbWVhbihEYXRhc2V0TmFtZSRDb2x1bW5OYW1lLG5hLnJtID0gVFJVRSkKYGBgCgojIyMjIENyZWF0aW5nIFRlc3RpbmcgYW5kIFRyYWluaW5nIFNldHMKT25jZSB5b3XigJl2ZSBwcmVwcm9jZXNzZWQgeW91ciBkYXRhc2V0LCBpdOKAmXMgbm93IHRpbWUgdG8gY3JlYXRlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMgZm9yIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB5b3Ugd2lsbCBiZSBjcmVhdGluZy4gCgpIb3cgdG8gYmVnaW4/IEluIG9yZGVyIHRvIGNyZWF0ZSB5b3VyIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMsIHlvdSBuZWVkIHRvIHVzZSB0aGUgc2V0LnNlZWQoKSBmdW5jdGlvbi4gVGhlIHNlZWQgaXMgYSBudW1iZXIgdGhhdCB5b3UgY2hvb3NlIGZvciBhIHN0YXJ0aW5nIHBvaW50IHVzZWQgdG8gY3JlYXRlIGEgc2VxdWVuY2Ugb2YgcmFuZG9tIG51bWJlcnMuIEl0IGlzIGFsc28gaGVscGZ1bCBmb3Igb3RoZXJzIHdobyB3YW50IHRvIHJlY3JlYXRlIHlvdXIgc2FtZSByZXN1bHRzLiBIZXJlIGlzIHRoZSBmdW5jdGlvbjoKClRJUDoKQSBjb21tb24gc2V0LnNlZWQgbnVtYmVyIGlzIDEyMy4gVG8gdXNlIHRoZSBzYW1lIHNldCBvZiByYW5kb20gbnVtYmVycywgeW914oCZbGwgd2FudCB0byB1c2UgdGhlIHNhbWUgc2VlZCBudW1iZXIgdGhyb3VnaG91dCB5b3VyIG1vZGVsaW5nIHByb2Nlc3MuCgpgYGB7cn0Kc2V0LnNlZWQoMSkKYGBgCgpIb3cgZG8geW91IHNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cz8gWW914oCZbGwgbm93IHdhbnQgdG8gc3BsaXQgeW91ciBkYXRhIGludG8gdHdvIHNldHMgZm9yIG1vZGVsaW5nLiBPbmUgaXMgdGhlIHRyYWluaW5nIHNldCBhbmQgdGhlIG90aGVyIG9uZSBiZWluZyB0aGUgdGVzdCBzZXQuIEEgY29tbW9uIHNwbGl0IGlzIDcwLzMwLCB3aGljaCBtZWFucyB0aGF0IDcwJSBvZiB0aGUgZGF0YSB3aWxsIGJlIHRoZSB0cmFpbmluZyBzZXTigJlzIHNpemUgYW5kIDMwJSBvZiB0aGUgZGF0YSB3aWxsIGJlIHRoZSB0ZXN0IHNldOKAmXMgc2l6ZS4gWW91IHdpbGwgYmUgdXNpbmcgdGhlIDcwLzMwIHNwbGl0LCBidXQgYW5vdGhlciBjb21tb24gc3BsaXQgaXMgODAvMjAuCgpTZXR0aW5nIHRoZSB0cmFpbmluZyBzZXTigJlzIHNpemUgYW5kIHRoZSB0ZXN0aW5nIHNldOKAmXMgc2l6ZSBjYW4gYmUgZG9uZSBieSBwZXJmb3JtaW5nIHRoZXNlIHR3byBsaW5lcyBvZiBjb2RlLiBUaGVzZSB0d28gbGluZXMgY2FsY3VsYXRlIHRoZSBzaXplcyBvZiBlYWNoIHNldCBidXQgZG8gbm90IGNyZWF0ZSB0aGUgc2V0czoKCmBgYHtyfQp0cmFpbl90ZXN0X3NpemU8LWZ1bmN0aW9uKERhdGFzZXROYW1lLHRyYWluaW5nX3NpemVfcmF0aW89MC43KXsKICB0cmFpblNpemU8LXJvdW5kKG5yb3coRGF0YXNldE5hbWUpKnRyYWluaW5nX3NpemVfcmF0aW8pIAogIHRlc3RTaXplPC1ucm93KERhdGFzZXROYW1lKS10cmFpblNpemUKICBkZl9vdXQgPSBkYXRhLmZyYW1lKCd0cmFpblNpemUnPWModHJhaW5TaXplKSwndGVzdFNpemUnPWModGVzdFNpemUpKQogIHJldHVybihkZl9vdXQpCn0KCnRyYWluX3Rlc3Rfc2l6ZShkZl9jYXJzLDAuNykKYGBgCmBgYHtyfQp0cmFpbl90ZXN0X3NpemUoZGZfaXJpcywwLjcpCmBgYAoKSG93IGRvIHlvdSBjcmVhdGUgdGhlIHRyYWluaW5nIGFuZCB0ZXN0IHNldHM/IEl04oCZcyBub3cgdGltZSBmb3IgeW91IHRvIGNyZWF0ZSB0aGUgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cy4gV2UgYWxzbyB3YW50IHRoZXNlIHNldHMgdG8gYmUgaW4gYSByYW5kb21pemVkIG9yZGVyLCB3aGljaCB3aWxsIGNyZWF0ZSB0aGUgbW9zdCBvcHRpbWFsIG1vZGVsLgoKVG8gcGVyZm9ybSB0aGlzLCB5b3UgbmVlZCB0byBydW4gdGhlc2UgbGluZXMgb2YgY29kZS4gVHlwZSBpbiB0aGlzIGNvZGUgaW50byBSIFNjcmlwdCBvciBDb25zb2xlOgoKYGBge3J9CnRyYWluX3Rlc3Rfc3BsaXQ8LWZ1bmN0aW9uKERhdGFzZXROYW1lLHRyYWluaW5nX3NpemVfcmF0aW89MC43KXsKICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgZGZfdHJhaW5fdGVzdF9zaXplIDwtIHRyYWluX3Rlc3Rfc2l6ZShkZl9jYXJzLHRyYWluaW5nX3NpemVfcmF0aW8pICAjZ2V0IHRyYWluIHRlc3Qgc2l6ZXMKCiAgdHJhaW5TaXplPC1kZl90cmFpbl90ZXN0X3NpemVbMSwxXQogIAogIHRyYWluaW5nX2luZGljZXM8LXNhbXBsZShzZXFfbGVuKG5yb3coRGF0YXNldE5hbWUpKSxzaXplID0gdHJhaW5TaXplKQogIAogIHRyYWluU2V0PC1EYXRhc2V0TmFtZVt0cmFpbmluZ19pbmRpY2VzLF0KICAKICB0ZXN0U2V0PC1EYXRhc2V0TmFtZVstdHJhaW5pbmdfaW5kaWNlcyxdIAogIAogIHJldHVybihsaXN0KHRyYWluU2V0LHRlc3RTZXQpKQoKfQoKbGlzdF9kZl90cmFpbl90ZXN0X3NldHMgPSB0cmFpbl90ZXN0X3NwbGl0KGRmX2NhcnMsMC43KQpkZl90cmFpblNldCA9IGxpc3RfZGZfdHJhaW5fdGVzdF9zZXRzW1sxXV0KcHJpbnQoZGZfdHJhaW5TZXQpCgpkZl90ZXN0U2V0ID0gbGlzdF9kZl90cmFpbl90ZXN0X3NldHNbWzJdXQpwcmludChkZl90ZXN0U2V0KQpgYGAKCiMjIyA1LiBXYWxrIFRocm91Z2ggdGhlIFIgVHV0b3JpYWwgLSBQYXJ0IDMKCldpdGhpbiB0aGlzIHNlY3Rpb24gb2YgdGhlIHR1dG9yaWFsLCB5b3Ugd2lsbCBsZWFybiBob3cgdG8gY3JlYXRlIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwsIHVuZGVyc3RhbmQgaXRzIG91dHB1dCBhbmQgdXNlIHRoZSBwcmVkaWN0aW9uIGZ1bmN0aW9uLgoKIyMjIExpbmVhciBSZWdyZXNzaW9uIE1vZGVsCllvdeKAmXJlIG5vdyByZWFkeSB0byBydW4geW91ciBkYXRhIHRocm91Z2ggeW91ciBtb2RlbGluZyBhbGdvcml0aG0uIFRoZSBtb2RlbCB0aGF0IHdlIHdpbGwgYmUgdXNpbmcgaXMgdGhlIExpbmVhciBSZWdyZXNzaW9uIE1vZGVsLCB3aGljaCBpcyBoZWxwZnVsIHdoZW4gdHJ5aW5nIHRvIGRpc2NvdmVyIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0d28gdmFyaWFibGVzLiBUaGVzZSB0d28gdmFyaWFibGVzIHJlcHJlc2VudCB0aGUgWCBhbmQgWSB3aXRoaW4gdGhlIGxpbmVhciBlcXVhdGlvbi4gVGhlIFggdmFyaWFibGUgaXMgdGhlIHByZWRpY3RvciB2YXJpYWJsZSwgYWxzbyBrbm93biBhcyB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGUgYmVjYXVzZSBpdCBkb2VzbuKAmXQgZGVwZW5kIG9uIG90aGVyIGF0dHJpYnV0ZXMgd2hpbGUgbWFraW5nIHByZWRpY3Rpb25zLiBZIGlzIHRoZSByZXNwb25zZSB2YXJpYWJsZSwgYWxzbyBrbm93biBhcyB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGJlY2F1c2UgaXRzIHZhbHVlIGRlcGVuZHMgb24gdGhlIG90aGVyIHZhcmlhYmxlcy4gIChXZSB3aWxsIGJlIGtlZXBpbmcgdGhpcyBhdCBhIGhpZ2ggbGV2ZWwuIElmIHlvdeKAmWQgbGlrZSB0byBkaXNjb3ZlciBtb3JlIGFib3V0IHRoaXMgZXF1YXRpb24sIHBsZWFzZSBmZWVJIGZyZWUgdG8gZG8geW91ciBvd24gcmVzZWFyY2guKSBJbiBvdXIgY2FzZSwgdGhlc2UgdHdvIHZhcmlhYmxlcyB3aWxsIGJlIFNwZWVkIGFuZCBEaXN0YW5jZS4gV2UgYXJlIHRyeWluZyB0byBwcmVkaWN0IERpc3RhbmNlLCBzbyBpdCBpcyBvdXIgZGVwZW5kZW50L3Jlc3BvbnNlL1kgdmFyaWFibGUuIFNwZWVkIGlzIG91ciBpbmRlcGVuZGVudC9wcmVkaWN0b3IvWCB2YXJpYWJsZS4gCgpgYGB7cn0KcGxvdChkZl9jYXJzJCdzcGVlZF9vZl9jYXInLGRmX2NhcnMkJ2Rpc3RhbmNlX29mX2NhcicseGxhYiA9ICdzcGVlZF9vZl9jYXInLCB5bGFiID0gJ2Rpc3RhbmNlX29mX2NhcicsbWFpbj0ndHJhaW5pbmcrdGVzdCBzZXQnKQpwbG90KGRmX3RyYWluU2V0JCdzcGVlZF9vZl9jYXInLGRmX3RyYWluU2V0JCdkaXN0YW5jZV9vZl9jYXInLHhsYWIgPSAnc3BlZWRfb2ZfY2FyJywgeWxhYiA9ICdkaXN0YW5jZV9vZl9jYXInLG1haW49J2RmX3RyYWluU2V0JykKcGxvdChkZl90ZXN0U2V0JCdzcGVlZF9vZl9jYXInLGRmX3Rlc3RTZXQkJ2Rpc3RhbmNlX29mX2NhcicseGxhYiA9ICdzcGVlZF9vZl9jYXInLCB5bGFiID0gJ2Rpc3RhbmNlX29mX2NhcicsbWFpbj0ndGVzdFNldCcpCmBgYAoKVG8gY3JlYXRlIHRoaXMgbW9kZWwsIHdlIHdpbGwgYmUgdXNpbmcgdGhlIGxpbmVhciBtb2RlbCBmdW5jdGlvbiDigJMgbG0oKS4gSGVyZSBpcyB0aGUgYmFzaWMgbGluZSBvZiBjb2RlIGZvciB0aGUgbGluZWFyIG1vZGVsIGZ1bmN0aW9uLiAKCmBgYHtyfQpsbV9jYXJzX3NwZWVkX3ZzX2Rpc3RhbmNlPC1sbShkaXN0YW5jZV9vZl9jYXIgfiBzcGVlZF9vZl9jYXIsIGRmX3RyYWluU2V0KQpgYGAKCkRpZCB5b3UgY3JlYXRlIGFuIG9wdGltYWwgbW9kZWw/IFRvIHNlZSBrZXkgbWV0cmljcyBvZiB5b3VyIG1vZGVsLCB0eXBlIGluIHRoaXMgY29kZSBpbnRvIFIgU2NyaXB0IG9yIENvbnNvbGUKCmBgYHtyfQpzdW1tYXJ5KGxtX2NhcnNfc3BlZWRfdnNfZGlzdGFuY2UpCmBgYApUaGUgdHdvIG1ldHJpY3MgdGhhdCB3ZSB3aWxsIGJlIGRpc2N1c3NpbmcgYXJlOgoKKiBNdWx0aXBsZSBSLXNxdWFyZWQtIEhvdyB3ZWxsIHRoZSByZWdyZXNzaW9uIGxpbmUgZml0cyB0aGUgZGF0YSAoMSBtZWFucyBpdOKAmXMgYSBwZXJmZWN0IGZpdCkuCiogcC12YWx1ZSAtIFRlbGxzIHlvdSBob3cgbXVjaCB0aGUgSW5kZXBlbmRlbnQgVmFyaWFibGUvUHJlZGljdG9yIGFmZmVjdHMgdGhlIERlcGVuZGVudCBWYXJpYWJsZS9SZXNwb25zZS8uIEEgcC12YWx1ZSBvZiBtb3JlIHRoYW4gMC4wNSBtZWFucyB0aGUgSW5kZXBlbmRlbnQgVmFyaWFibGUgaGFzIG5vIGVmZmVjdCBvbiB0aGUgRGVwZW5kZW50IFZhcmlhYmxlOyBsZXNzIHRoYW4gMC4wNSBtZWFucyB0aGUgcmVsYXRpb25zaGlwIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuCiogSWYgeW914oCZZCBsaWtlIHRvIGxlYXJuIG1vcmUgYWJvdXQgQWRqdXN0ZWQgUi1zcXVhcmVkIGFuZCB0aGUgRi1zdGF0aXN0aWMsIHNlZSB0aGUgUmVzb3VyY2VzIHRhYi4KRm9yIG5vdywgeW91IHdpbGwgYmUgbGVhdmluZyB0aGlzIG1vZGVsIGFzIGlzLiBZb3Ugd2lsbCBsZWFybiBob3cgdG8gYWRqdXN0IHlvdXIgbW9kZWzigJlzIHBhcmFtZXRlcnMgdG8gY3JlYXRlIHRoZSBtb3N0IG9wdGltYWwgbW9kZWwgaW4gbGF0ZXIgY291cnNlcy4gCgojIyMjIFByZWRpY3Rpb25zClRoZSBuZXh0IHN0ZXAgaXMgdG8gcHJlZGljdCB0aGUgY2FycyBkaXN0YW5jZXMgdGhyb3VnaCB0aGUgc3BlZWQgb2YgdGhlIGNhcnMuIFRvIGRvIHRoaXMsIHdl4oCZbGwgYmUgdXNpbmcgdGhlIHByZWRpY3Rpb24gZnVuY3Rpb24g4oCTIHByZWRpY3QoKSAKYGBge3J9CnByZWRpY3RfbG1fY2Fyc192c19kaXN0YW5jZSA8LSBwcmVkaWN0KGxtX2NhcnNfc3BlZWRfdnNfZGlzdGFuY2UsZGZfdGVzdFNldCkKcHJpbnQoInByZWRpY3Rpb25zOiIpCnByZWRpY3RfbG1fY2Fyc192c19kaXN0YW5jZQpgYGAKYGBge3J9CnBsb3QoZGZfdHJhaW5TZXQkc3BlZWRfb2ZfY2FyLCBkZl90cmFpblNldCRkaXN0YW5jZV9vZl9jYXIsIHhsYWIgPSAnc3BlZWQgb2YgY2FyJywgeWxhYiA9ICdkaXN0YW5jZSBvZiBjYXInLCBjb2w9J3JlZCcscGNoID0gMTkpCmFibGluZShsbShkaXN0YW5jZV9vZl9jYXIgfiBzcGVlZF9vZl9jYXIsIGRmX3RyYWluU2V0KSxjb2w9J3JlZCcpCnBvaW50cyhkZl90ZXN0U2V0JHNwZWVkX29mX2NhciwgZGZfdGVzdFNldCRkaXN0YW5jZV9vZl9jYXIsY29sPSdibHVlJykKbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kPWMoIlRyYWluU2V0ICYgTGluZWFyIE1vZGVsIiwiVGVzdFNldCIpLAogICAgICAgY29sPWMoInJlZCIsImJsdWUiKSxsdHk9MSwgY2V4PS45KQpgYGAKClRJUDoKTWFrZSBhIG5vdGUgb2YgeW91ciBwcmVkaWN0aW9ucy4gKFlvdSB3aWxsIG5lZWQgdG8gaW5jbHVkZSB0aGVtIGluIHlvdXIgaW5mb3JtYWwgcmVwb3J0IHRvIEJsYWNrd2VsbC4pCgojIyMgRmluZCB0aGUgRXJyb3JzIGluIGFuIFIgU2NyaXB0cwpDb25ncmF0cywgeW914oCZdmUgbm93IGNvbXBsZXRlZCBhIFJlZ3Jlc3Npb24gQW5hbHlzaXMgaW4gUiEKCkJsYWNrd2VsbCBiZWxpZXZlcyB0aGF0IHJlYWRpbmcgYW5kIGRlY2lwaGVyaW5nIGNvZGUgaXMganVzdCBhcyBpbXBvcnRhbnQgYXMgY29kaW5nIGl0c2VsZi4gVGhlcmVmb3JlLCBpdOKAmXMgbm93IHRpbWUgdG8gdGVzdCB5b3VyIG5ldy1mb3VuZCBrbm93bGVkZ2Ugb2YgUiBieSBydW5uaW5nIGEgc2NyaXB0IG9mIGNvZGUuIEJld2FyZSB0aGF0IHlvdSB3aWxsIGJlIGVuY291bnRlcmluZyBlcnJvcnMgYW5kIHdhcm5pbmdzLCBidXQgeW91IGhhdmUgdGhlIHBvd2VyIHRvIG92ZXJjb21lIHRoZXNlISAKCldoYXQgZG9lcyBhbiBlcnJvciBsb29rIGxpa2U/IEFuIGVycm9yIG1lc3NhZ2Ugd2lsbCBiZSBpbiByZWQgYW5kIHdpbGwgcHJldmVudCB5b3UgZnJvbSBnb2luZyBmdXJ0aGVyIGR1ZSB0byBhIG1pc3Rha2UgaW4gdGhlIHByZXZpb3VzIGxpbmUgb3IgbGluZXMgb2YgY29kZS4KCldoYXQgZG9lcyBhIHdhcm5pbmcgbWVzc2FnZSBsb29rIGxpa2U/IEEgd2FybmluZyBtZXNzYWdlIHdpbGwgYWxzbyBiZSBpbiByZWQuIFlvdSB3aWxsIHN0aWxsIGJlIGFibGUgdG8gY29udGludWUsIGJ1dCBpdCBtaWdodCBvciBtaWdodCBub3QgYWZmZWN0IHlvdXIgYW5hbHlzaXMgaW4gdGhlIGxvbmcgcnVuLgoKWW914oCZdmUgZW5jb3VudGVyZWQgYW4gZXJyb3Ivd2FybmluZyBtZXNzYWdlLCB3aGF0IG5vdz8KClJldmlldyB5b3VyIHR1dG9yaWFsLgpDaGVjayB5b3VyIHNwZWxsaW5nL3NwYWNpbmcuClJlc2VhcmNoIHRoZSBlcnJvci93YXJuaW5nIG1lc3NhZ2Ug4oCUIEhhcyBhbnlvbmUgZWxzZSBlbmNvdW50ZXJlZCB0aGlzIGVycm9yPyAKVElQOgpSRU1FTUJFUjoKCkRvbuKAmXQgZm9yZ2V0IHlvdXIgYW5hbHlzaXMgZ29hbCEKSWYgeW91IGFyZSB0eXBpbmcgaW4gdGhlIENvbnNvbGUsIHByZXNzIEVOVEVSIHRvIHNlZSBydW4geW91ciBjb2RlLgpJZiB5b3UgYXJlIHR5cGluZyBpbiB0aGUgUiBTY3JpcHQsIHByZXNzIENPTU1BTkQgKyBFTlRFUiB0byBzZWUgcnVuIHlvdXIgY29kZS4KVGhlIGRhdGFzZXQgdGhhdCBpcyBkaXNjdXNzZWQgaW4gdGhlIGNvZGUgY29uY2VybnMgdGhlIElyaXMgZmxvd2VyLCB3aGljaCBpcyB3aXRoaW4gdGhlIHppcCBmaWxlIGF0dGFjaGVkIHRvIERhbmllbGxlJ3MgZW1haWwuIFRoZSBhbmFseXNpcyBnb2FsIGlzIHRvIHByZWRpY3QgYSBwZXRhbCdzIGxlbmd0aCB1c2luZyB0aGUgcGV0YWzigJlzIHdpZHRoLiAgCgpIZXJlIGlzIHRoZSBzY3JpcHQgdGhhdCB5b3Ugd2lsbCBydW4gKGFuZCBmaXggYW55IGVycm9ycyBpdCBjb250YWlucyk6IApgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcyhyZWFkcikgIyBPcmlnaW5hbCBDb2RlCmBgYApgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygncmVhZHInKSAjIE5ldyBDb2RlIHwgTm90ZTogbWFrZSBzdXJlIHlvdSB1c2UgcXVvdGVzIHdoZW4gY2FsbGluZyBhIHBhY2thZ2UKYGBgCmBgYHtyfQojbGlicmFyeSjigJxyZWFkcuKAnSkgIyBPcmlnaW5hbCBDb2RlCiNFcnJvcjogdW5leHBlY3RlZCBpbnB1dCBpbiAibGlicmFyeSjvv70iCgpsaWJyYXJ5KHJlYWRyKSAjIE5ldyBDb2RlIHwgTm90ZTogcmVmZXJlbmNlIGJ5IHVucXVvdGVkIG5hbWUgd2hlbiByZWFkaW5nIGxpYnJhcnkgcGFja2FnZS4KYGBgCgpgYGB7cn0KI0lyaXNEYXRhc2V0IDwtIHJlYWQuY3N2KGlyaXMuY3N2KSAjIE9yaWdpbmFsIENvZGUKI0Vycm9yIGluIHJlYWQudGFibGUoZmlsZSA9IGZpbGUsIGhlYWRlciA9IGhlYWRlciwgc2VwID0gc2VwLCBxdW90ZSA9IHF1b3RlLCA6IG9iamVjdCAnaXJpcy5jc3YnIG5vdCBmb3VuZAoKSXJpc0RhdGFzZXQgPC0gcmVhZC5jc3YoIlIgVHV0b3JpYWwgRGF0YSBTZXRzL2lyaXMuY3N2IikgIyBOZXcgQ29kZSB8IE5vdGU6IHBvaW50IHRvIGRpcmVjdG9yeSBsb2NhdGlvbiB1c2luZyBxdW90ZXMKYGBgCgpgYGB7cn0KYXR0cmlidXRlcyhJcmlzRGF0YXNldCkgI09yaWdpbmFsIGNvZGUKYGBgCmBgYHtyfQojc3VtbWFyeShyaXNEYXRhc2V0KSAjT3JpZ2luYWwgY29kZQojRXJyb3IgaW4gc3VtbWFyeShyaXNEYXRhc2V0KSA6IG9iamVjdCAncmlzRGF0YXNldCcgbm90IGZvdW5kCgpzdW1tYXJ5KElyaXNEYXRhc2V0KSAjIG5ldyBjb2RlIHwgbm90ZTogbWlzc2luZyAiSSIgYXQgc3RhcnQgb2YgImlyaXMuLi4iCmBgYAoKYGBge3J9CiMgc3RyKElyaXNEYXRhc2V0cykgIyBPcmlnaW5hbCBjb2RlCiMgRXJyb3IgaW4gc3RyKElyaXNEYXRhc2V0cykgOiBvYmplY3QgJ0lyaXNEYXRhc2V0cycgbm90IGZvdW5kCgpzdHIoSXJpc0RhdGFzZXQpICMgbmV3IGNvZGUgfCB0eXBvCmBgYAoKYGBge3J9Cm5hbWVzKElyaXNEYXRhc2V0KSAjb3JpZ2luYWwgY29kZQpgYGAKYGBge3J9CiMgaGlzdChJcmlzRGF0YXNldCRTcGVjaWVzKSAjb3JpZ2luYWwgY29kZSAKIyBFcnJvciBpbiBoaXN0LmRlZmF1bHQoSXJpc0RhdGFzZXQkU3BlY2llcykgOiAneCcgbXVzdCBiZSBudW1lcmljCgpoaXN0KElyaXNEYXRhc2V0JFNlcGFsLkxlbmd0aCkgI25ldyBjb2RlIHwgaGlzdG9ncmFtIGNhbiBvbmx5IGFjY2VwdCBudW1lcmljIGNvbHVtbnMKYGBgCgpgYGB7cn0KIyBwbG90KElyaXNEYXRhc2V0JFNlcGFsLkxlbmd0aCAjb3JpZ2luYWwgY29kZQojIEVycm9yOiBJbmNvbXBsZXRlIGV4cHJlc3Npb246IHBsb3QoSXJpc0RhdGFzZXQkU2VwYWwuTGVuZ3RoICNvcmlnaW5hbCBjb2RlCgpwbG90KElyaXNEYXRhc2V0JFNlcGFsLkxlbmd0aCkgI25ldyBjb2RlIHwgbWlzc2luZyBlbmQgYnJhY2tldApgYGAKCmBgYHtyfQojcXFub3JtKElyaXNEYXRhc2V0KSAjb3JpZ2luYWwgY29kZQojRXJyb3IgaW4gRlVOKFhbW2ldXSwgLi4uKSA6IG9ubHkgZGVmaW5lZCBvbiBhIGRhdGEgZnJhbWUgd2l0aCBhbGwgbnVtZXJpYyB2YXJpYWJsZXMKCnFxbm9ybShJcmlzRGF0YXNldCRTZXBhbC5MZW5ndGgpICNvcmlnaW5hbCBjb2RlIHwgbXVzdCBzZWxlY3QgYSBzcGVjaWZpYyBudW1lcmljIGNvbHVtbiBmb3IgUS1RIHBsb3QKYGBgCgpgYGB7cn0KSXJpc0RhdGFzZXQkU3BlY2llczwtIGFzLm51bWVyaWMoSXJpc0RhdGFzZXQkU3BlY2llcykgICMgb3JpZ2luYWwgY29kZQpgYGAKCmBgYHtyfQpzZXQuc2VlZCgxMjMpICMgb3JpZ2luYWwgY29kZQpgYGAKCmBgYHtyfQp0cmFpblNpemUgPC0gcm91bmQobnJvdyhJcmlzRGF0YXNldCkgKiAwLjIpIyBvcmlnaW5hbCBjb2RlCnRyYWluU2l6ZQpgYGAKCmBgYHtyfQp0ZXN0U2l6ZSA8LSBucm93KElyaXNEYXRhc2V0KSAtIHRyYWluU2V0ICMgb3JpZ2luYWwgY29kZQp0ZXN0U2l6ZQpgYGAKYGBge3J9CnRlc3RTaXplIDwtIHJvdW5kKG5yb3coSXJpc0RhdGFzZXQpKSAtIHRyYWluU2l6ZSAjIG5ldyBjb2RlIHwgcmVwbGFjZSB0cmFpblNldCB3aXRoIHRyYWluU2l6ZQp0ZXN0U2l6ZQpgYGAKYGBge3J9CiMgdHJhaW5TaXplcyAjIG9yZ2lpbmFsIGNvZGUKIyBFcnJvcjogb2JqZWN0ICd0cmFpblNpemVzJyBub3QgZm91bmQKCnRyYWluU2l6ZSAjIG5ldyBjb2RlIHwgZHJvcCAicyIgZnJvbSBvcmlnaW5hbCBjb2RlCmBgYAoKYGBge3J9CnRlc3RTaXplICNvcmlnaW5hbCBjb2RlCmBgYApgYGB7cn0KdHJhaW5TZXQgPC0gSXJpc0RhdGFzZXRbdHJhaW5pbmdfaW5kaWNlcywgXSAjb3JpZ2luYWwgY29kZQpgYGAKCmBgYHtyfQojbmV3IGNvZGUKdHJhaW5pbmdfaW5kaWNlczwtc2FtcGxlKHNlcV9sZW4obnJvdyhJcmlzRGF0YXNldCkpLHNpemUgPSB0cmFpblNpemUpICNnZXQgaW5kaWNlcwoKdHJhaW5TZXQ8LUlyaXNEYXRhc2V0W3RyYWluaW5nX2luZGljZXMsXQpwcmludCh0cmFpblNldCkKYGBgCmBgYHtyfQp0ZXN0U2V0IDwtIElyaXNEYXRhc2V0Wy10cmFpbmluZ19pbmRpY2VzLCBdI29yaWdpbmFsIGNvZGUKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoNDA1KSNvcmlnaW5hbCBjb2RlCmBgYAoKYGBge3J9CnRyYWluU2V0IDwtIElyaXNEYXRhc2V0W3RyYWluaW5nX2luZGljZXMsIF0gI29yaWdpbmFsIGNvZGUgKHJlcHJlYXRlZCBwcmV2aW91c2x5KQp0ZXN0U2V0IDwtIElyaXNEYXRhc2V0Wy10cmFpbmluZ19pbmRpY2VzLCBdICNvcmlnaW5hbCBjb2RlIChyZXByZWF0ZWQgcHJldmlvdXNseSkKYGBgCgpgYGB7cn0KIyBMaW5lYXJNb2RlbDwtIGxtKHRyYWluU2V0JFBldGFsLldpZHRoIH4gdGVzdGluZ1NldCRQZXRhbC5MZW5ndGgpICNvcmlnaW5hbCBjb2RlCiMgRXJyb3IgaW4gZXZhbChwcmVkdmFycywgZGF0YSwgZW52KSA6IG9iamVjdCAndGVzdGluZ1NldCcgbm90IGZvdW5kCgpMaW5lYXJNb2RlbDwtIGxtKFBldGFsLldpZHRoIH4gUGV0YWwuTGVuZ3RoLCB0cmFpblNldCkgI25ldyBjb2RlIHwgYm90aCBkYXRhIHNldHMgbXVzdCBjb21lIGZyb20gc2FtZSBkYXRhIGZyYW1lCmBgYAoKYGBge3J9CnN1bW1hcnkoTGluZWFyTW9kZWwpICMgb3JpZ2luYWwgY29kZQpgYGAKCmBgYHtyfQojIHByZWRpY3Rpb248LXByZWRpY3QoTGluZWFyTW9kZWx0ZXN0U2V0KSAjb3JpZ25hbCBjb2RlCiMgRXJyb3IgaW4gcHJlZGljdChMaW5lYXJNb2RlbHRlc3RTZXQpIDogb2JqZWN0ICdMaW5lYXJNb2RlbHRlc3RTZXQnIG5vdCBmb3VuZAoKcHJlZGljdGlvbjwtcHJlZGljdChMaW5lYXJNb2RlbCx0ZXN0U2V0KSAjbmV3IGNvZGUKYGBgCgpgYGB7cn0KIyBwcmVkaWN0aW9ucyAjb3JpZ2luYWwgY29kZQojIEVycm9yOiBvYmplY3QgJ3ByZWRpY3Rpb25zJyBub3QgZm91bmQKCnByZWRpY3Rpb24gI25ldyBjb2RlCmBgYAoKYGBge3J9CnBsb3QodHJhaW5TZXQkUGV0YWwuTGVuZ3RoLCB0cmFpblNldCRQZXRhbC5XaWR0aCwgeGxhYiA9ICdQZXRhbC5MZW5ndGgnLCB5bGFiID0gJ1BldGFsLldpZHRoJywgY29sPSdyZWQnLHBjaCA9IDE5KQphYmxpbmUobG0oUGV0YWwuV2lkdGggfiBQZXRhbC5MZW5ndGgsIHRyYWluU2V0KSxjb2w9J3JlZCcpCnBvaW50cyh0ZXN0U2V0JFBldGFsLkxlbmd0aCwgdGVzdFNldCRQZXRhbC5XaWR0aCxjb2w9J2JsdWUnKQpsZWdlbmQoInRvcGxlZnQiLCBsZWdlbmQ9YygiVHJhaW5TZXQgJiBMaW5lYXIgTW9kZWwiLCJUZXN0U2V0IiksCiAgICAgICBjb2w9YygicmVkIiwiYmx1ZSIpLGx0eT0xLCBjZXg9MC45KQpgYGAKCiMjIyA3LiBXcml0ZSBhbmQgSW5mb3JtYWwgUmVwb3J0Ck5vdyB0aGF0IHlvdeKAmXZlIGNvbXBsZXRlZCB0aGUgUiB0dXRvcmlhbCBhbmQgdGhlIEZpbmQgVGhlIEVycm9ycyB0YXNrcywgd3JpdGUgYW4gaW5mb3JtYWwgcmVwb3J0IGluIFdvcmQgdG8gRGFuaWVsbGUgZnJvbSBCbGFja3dlbGwuCgpZb3VyIHJlcG9ydCBzaG91bGQgaW5jbHVkZSB0aGUgZm9sbG93aW5nOiAKCllvdXIgcHJlZGljdGlvbnMgY29uY2VybmluZyBob3cgZmFyIGEgY2VydGFpbiBjYXIgY2FuIHRyYXZlbCBiYXNlZCBvbiBzcGVlZC4gKEZyb20gdGhlIFIgdHV0b3JpYWwuKQpZb3VyIHByZWRpY3Rpb25zIGNvbmNlcm5pbmcgdGhlIHBldGFsIGxlbmd0aCB0aHJvdWdoIHVzaW5nIHRoZSBwZXRhbOKAmXMgd2lkdGguIChGcm9tIHRoZSBGaW5kIHRoZSBFcnJvcnMgdGFzay4pClRoZSBlcnJvcnMvd2FybmluZyBtZXNzYWdlcyB0aGF0IHlvdSBlbmNvdW50ZXJlZCBhbmQgaG93IHlvdSBvdmVyY2FtZSB0aGVtLgpJbiBhZGRpdGlvbiwgdGhpbmsgYWJvdXQgYWRkaW5nIHRoZSBhbnN3ZXJzIHRvIHRoZXNlIHF1ZXN0aW9ucyB3aXRoaW4geW91ciByZXBvcnQ6CgpXYXMgaXQgc3RyYWlnaHRmb3J3YXJkIHRvIGluc3RhbGwgUiBhbmQgUlN0dWRpbz8KV2FzIHRoZSB0dXRvcmlhbCB1c2VmdWw/IFdvdWxkIHlvdSByZWNvbW1lbmQgaXQgdG8gb3RoZXJzPwpXaGF0IGFyZSB0aGUgbWFpbiBsZXNzb25zIHlvdSd2ZSBsZWFybmVkIGZyb20gdGhpcyBleHBlcmllbmNlPwpXaGF0IHJlY29tbWVuZGF0aW9ucyB3b3VsZCB5b3UgZ2l2ZSB0byBvdGhlciBlbXBsb3llZXMgd2hvIG5lZWQgdG8gZ2V0IHN0YXJ0ZWQgdXNpbmcgUiBhbmQgZG9pbmcgcHJlZGljdGl2ZSBhbmFseXRpY3MgaW4gUiBpbnN0ZWFkIG9mIFJhcGlkbWluZXI/CgoqKioKKioqCiMjIDxzcGFuIHN0eWxlPSdjb2xvcjpyZWQnPiBHZXR0aW5nIFN0YXJ0ZWQgd2l0aCBSOiBJbmZvcm1hbCBSZXBvcnQgPC9zcGFuPgoKIyMjIFN1bW1hcnkKSW4gdGhpcyB0YXNrIHdlIGV4cGxvcmVkIHVzaW5nIFIgYXMgYSB0b29sIGZvciBkYXRhIHNjaWVuY2UvYW5hbHl0aWNzLiBDb21wYXJlZCB0byBSYXBpZCBNaW5lciwgUiBpcyBzaWduaWZpY2FudGx5IG1vcmUgcG93ZXJmdWwgYW5kIGZsZXhpYmxlLCB0aG91Z2ggaXQgbWF5IHNlZW0gbW9yZSBjb21wbGV4IHRvIG5ldyB1c2Vycy4gSW4gcGFydGljdWxhciwgdXRpbGl6aW5nIFIgdmlhIFJTdHVkaW8gZ2l2ZXMgdGhlIHVzZXIgYSBraW5kIG9mIGludGVncmFkZWQgZGV2ZWxvcG1lbnQgZW52aXJvbm1lbnQgKElERSkgZm9yIGRhdGEgc2NpZW5jZS4gVGhlIHRhc2sgY292ZXJlZCB0aGUgYmFzaWNzIG9mIGEgZGF0YSBzY2llbmNlIHBpcGVsaW5lOiBsb2FkaW5nIGtleSBtb2R1bGVzL2xpYnJhcmllcywgbG9hZGluZyBkYXRhLCBjbGVhbmluZyAmIHByZXByb2Nlc3NpbmcgZGF0YSwgYXBwbHkgYSB0cmFpbi10ZXN0IHNwbGl0LCB0cmFpbmluZyBhIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwgKE1MKSwgYW5kIG1ha2luZyBhIHByZWRpY3Rpb24gdXNpbmcgdGhlIE1MIG1vZGVsLiBPdmVyYWxsLCB0aGUgYWJpbGl0eSB0byBpbXBsZW1lbnQgdGhlc2Ugc3RlcHMgaW4gY29kZSBhbmQgdmlhIGEgY29tcHV0YXRpb25hbCBub3RlYm9vayBzdHlsZSwgbWFrZXMgZGF0YSByZXByb2R1Y2FiaWxpdHkgYW5kIG11Y2ggbW9yZSBlZmZpY2llbnQuIFRoaXMgaXMgYmVjYXVzZSB0aGUgbm90ZWJvb2sgc3R5bGUgb2YgZGF0YSBhbmFseXNpcyBhbGxvd3MgeW91IHRvIGludGVncmF0ZSB3cml0dGVuIHRleHQgd2l0aCBjb2RlIGFuZCBvdXRwdXQsIGFsbG93aW5nIHlvdSB0byBjcmVhdGUgYSBraW5kIG9mIHVzZXIgbWFudWFsIGZvciByZXByb2R1Y2luZyB5b3VyIHJlc3VsdHMuIEZ1cnRoZXJtb3JlLCB1dGlsaXppbmcgY29kZSB0byBwZXJmb3JtIGRhdGEgYW5hbHlzaXMgZ2l2ZXMgeW91IGFjY2VzcyB0byB1c2VmdWwgdHJpY2tzLCBzdWNoIGFzIGZvciBsb29wcywgaWYtdGhlbiBzdGF0ZW1lbnRzLCBjdXN0b20gZnVuY3Rpb25zLCBhbmQgbW9yZS4KCiMjIyBSZXN1bHRzClR3byBkYXRhIHNldHMgd2VyZSBhbmFseXplZDogKDEpICJjYXJzLmNzdiIgJiAoMikgImlyaXMuY3N2Ii4gSW4gYm90aCBkYXRhIHNldHMsIGRvdWJsZSAobnVtZXJpYyksIGFuZCBjYXRlZ29yaWNhbCBkYXRhIGNvbHVtbnMgd2VyZSBwcmVzZW50LCBob3dldmVyIGluIHRoaXMgdGFzayB3ZSBmb2N1c2VkIG9ubHkgb24gdGhlIG51bWVyaWMgZGF0YS4gU3BlY2lmaWNhbGx5LCBhZnRlciBzb21lIHByZWxpbWluYXJ5IGRhdGEgZXhwbG9yYXRpb24gdmlhIHNjYXR0ZXJwbG90cywgaGlzdG9ncmFtcywgYW5kIFEtUSBwbG90cywgd2UgcGVyZm9ybWVkIGEgNzAtMzAlIHRyYWluLXRlc3Qgc3BsaXQgYW5kIGZpdCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRvIHRoZSBkYXRhIHNldHMuCgpJbiB0aGUgY2FzZSBvZiAiY2Fycy5jc3YiLCB3ZSBhcHBsaWVkIGxpbmVhciByZWdyZXNzaW9uIHRvIHByZWRpY3QgdGhlIGRpc3RhbmNlIG9mIGEgY2FyZCwgZ2l2ZW4gaXQncyBzcGVlZC4gVGhlIHJlc3VsdCBvZiB0aGlzIGxpbmVhciBtb2RlbCBjYW4gYmUgc2VlbiBpbiB0aGUgZmlndXJlIGJlbG93LgohW0ZpZ3VyZSAxXShpbWFnZXMvZGlzdGFuY2VfdnNfc3BlZWQucG5nKQpIZXJlLCB3ZSBoYXZlIGhpZ2hsaWdodGVkIHRoZSBkYXRhIHBvaW50cyBmcm9tIHRoZSB0cmFpbmluZyBzZXQgaW4gcmVkLiBUaGUgbGluZWFyIG1vZGVsIHdhcyB0cmFpbmVkIG9uIHRoaXMgZGF0YSBzZXQsIHRodXMgdGhlIHByZWRpY2l0b24gbGluZSBpcyBhbHNvIGNvbG9yZWQgcmVkLiBUaGUgdGVzdCBzZXQgY2FuIGJlIGluIGJsdWUuIE5vdGUgdGhhdCB0aGUgdGVzdCBzZXQgY292ZXJzIGEgc2ltaWxhciBkaXN0cmlidXRpb24vcmFuZ2UgYXMgdGhlIHRyYWluaW5nIHNldCwgdGhvdWdoIHRoZXJlIGFyZSBtb3JlIGRhdGEgcG9pbnRzLiBUaGlzIGxpbmVhciBtb2RlbCBhY2hpZXZlIGFuICRSXjIkIHNjb3JlIG9mIDAuOTMgb24gdGhlIHRlc3Rpbmcgc2V0LCBpbXBseWluZyB0aGUgYSBnb29kIG1vZGVsIGZpdCwgYXMgY2FuIGJlIHNlZW4gZnJvbSB0aGUgcGxvdC4gSXQgc2hvdWxkIGJlIG5vdGVkIHRoYXQgb25lIGNvdWxkIGxpa2VseSB0cmFuc2Zvcm0gdGhlIHgtYXhpcywgcG9zc2libHkgdXNpbmcgYSBzZW1pLWxvZyBzY2FsZSBvZiBzb21lIGRlZ3JlZSwgdG8gZm9yY2UgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBkaXN0YW5jZSBvZiB0aGUgY2FyIGFuZCB0aGUgc3BlZWQgb2YgdGhlIGNhciB0byBiZWNvbWUgZXZlbnQgbW9yZSBsaW5lYXIuIFRoaXMgd291bGQgdGhlbiBnaXZlIGJldHRlIHByZWRpY3Rpb24gcmVzdWx0cyBpbiB0aGUgZXh0cmVtZXMgb2YgdGhlIGRhdGEgc2V0IGFuZCBwcm9iYWJseSBsZWFkIHRvIGJldHRlciBleHRyYXBvbGF0aW9uCgpGb2xsb3dpbmcgdGhlICJjYXJzLmNzdiIgZGF0YSBhbmFseXNpcywgd2UgcGVyZm9ybWVkIGFuIGFuYWx5c2lzIG9uIHRoZSAiaXJpcy5jc3YiIGRhdGEgc2V0LiBQYXJ0IG9mIHRoZSBhbmFseXNpcyBmb3IgdGhpcyBkYXRhIHNldCBpbnZvbHZlZCBkZWJ1Z2dpbmcgY29kZSBwcm92aWRlZCBieSB0aGUgaW5zdHJ1Y3Rvci4gVGhpcyBjb2RlLCBhbG9uZyB3aXRoIHRoZSBjb3JyZWN0ZWQgY29kZSwgY2FuIGJlIGZvdW5kIGluIHRoZSAiRmluZCB0aGUgRXJyb3JzIGluIGFuIFIgU2NyaXB0cyIgc2VjdGlvbiBvZiB0aGlzIG5vdGVib29rLiBUaGUgb3JpZ2luYWwgY29kZSBpcyBkZW5vdGVkIGJ5IHRoZSBjb21tZW50ICIjIG9yaWdpbmFsIGNvZGUiLCB0aGUgcmVzdWx0aW5nIGVycm9ycyBoYXZlIGJlZW4gcGFzdGVkIGJlbG93IHRoZSBvcmlnaW5hbCBjb2RlIGFzIGEgY29tbWVudCBhcyB3ZWxsLCBmb2xsb3dlZCBieSB0aGUgbmV3IGNvZGUgKCNuZXcgY29kZSkuIE1vc3Qgb2YgdGhlIGVycm9ycyB3ZXJlIHNpbXBseSBzeW50YWN0aWMgaW4gbmF0dXJlLiBUaGUgdHlwZXMgb2YgZXJyb3JzIGFyZSBtb3N0IGVhc2lseSBhdm9pZGVkIHVzaW5nIHRoZSBhdXRvZmlsbCAodGFiLW91dCkgZnVuY3Rpb25hbGl0eSBjb21tb24gdG8gbW9zdCBzY3JpcHRpbmcgbGFuZ3VhZ2VzLgoKQWZ0ZXIgZGVidWdnaW5nIHRoZSBwcm92aWRlZCBjb2RlLCB3ZSBwZXJmb3JtZWQgYW5vdGhlciBsaW5lYXIgcmVncmVzc2lvbiBmaXQsIGJ1dCB0aGlzIHRpbWUgdG8gdGhlICJpcmlzLmNzdiIgcGV0YWwgbGVuZ3RoIHZzLiBwZXRhbCB3aWR0aC4gVGhlIHJlc3VsdHMgb2YgdGhlIG1vZGVsIGFyZSBzaG93biBiZWxvdywgd2l0aCBjb2xvciBjb2RpbmcgaWRlbnRpY2FsIHRvIHRoZSBwcmV2aW91c2x5IGFuYWx5emVkIGZpZ3VyZS4KIVtGaWd1cmUgMl0oaW1hZ2VzL3BldGFsX3dpZHRoX3ZzX2xlbmd0aC5wbmcpCkhlcmUgd2Ugc2VlIGEgbGluZWFyIG1vZGVsIGFwcGVhcnMgdG8gcmVwcmVzZW50IHRoZSB0cnVlIG5hdHVyZSBvZiB0aGUgdHJlbmQgaW4gdGhlIGRhdGEgYmV0dGVyIHRoYW4gaXQgZGlkIGluIHRoZSBjYXIgY2FzZSwgaG93ZXZlciB0aGVyZSBpcyBhbHNvIG1vcmUgc3ByZWFkIGluIHRoZSBkYXRhIGZvciBhIGdpdmVuIHBldGFsIGxlbmd0aC4KCiMjIyBDb25jbHVkaW5nIENvbW1lbnRzCk92ZXJhbGwsIEkgZm91bmQgaXQgcHJldHR5IHN0cmFpZ2h0Zm9yd2FyZCB0byBkb3dubG9hZCBhbmQgaW5zdGFsbCBSIGFuZCBSU3R1ZGlvLiBNb3N0IG9mIHRoZSBhY3Rpdml0eSB3YXMgcHJldHR5IHN0cmFpZ2h0Zm9yd2FyZCwgaG93ZXZlciBJIGFsc28gaGF2ZSBhIGxvdCBvZiBleHBlcmllbmNlIHdpdGggbm90ZWJvb2tzIGdlbmVyYWxseSBhbmQgYSBmZXcgZGlmZmVyZW50IGNvZGluZyBsYW5ndWFnZXMsIGFsbCBvZiB3aGljaCBoYXZlIHRoZWlyIG93biB1bmlxdWUgJ2ZlYXR1cmVzJy4gT25lIHRoaW5nIEkgZW5qb3llZCB0aGUgbW9zdCB3YXMgYWN0dWFsbHkgZXhwbG9yaW5nIGJleW9uZCB3aGF0IHRoZSBhc3NpZ25tZW50IGV4cGxpY3RseSBhc2tlZC4gU3BlY2lmaWNhbGx5LCBJIGZvdW5kIGl0IHVzZWZ1bCBsb29raW5nIGludG8gaG93IHRvIGJ1aWxkIGN1c3RvbSBmdW5jdGlvbnMgaW4gUiBhbmQgaW1wbGVtZW50IGZvciBsb29wcy4gVGhpcyB3YXMgbXkgZmlyc3QgdGltZSB1c2luZyBSIGFuZCBvdmVyYWxsIEkgd291bGQgc2F5IGl0IGlzIHVzZWZ1bCBmb3IgZGF0YSBzY2llbnRpc3RzIHRvIGJlIGZhbWlsaWFyIHdpdGggUiwgYnV0IEkgaG9uZXN0bHkgd291bGQgbm90IHJlY29tbWVuZCBuZXcgZGF0YSBzY2llbnRpc3RzIHNwZW5kIHRvbyBtdWNoIHRpbWUgaW4gUi4gVGhpcyBpcyBwcmltYXJpbHkgYmVjYXVzZSBSIHNlZW1zIGEgYml0IGNsdW5reSBhbmQgbGVzcyBzY2FsYWJsZS9mbGV4aWJsZSBjb21wYXJlZCB0byBweXRob24uIEluIHBhcnRpY3VsYXIsIG1vcmUgYWR2YW5jZWQgbWFjaGluZSBsZWFybmluZyBtb2RlbHMsIHN1Y2ggYXMgbmV1cmFsIG5ldHdvcmtzLCBhcmUgbm90IGNvbW1vbmx5IGRldmVsb3BlZCBpbiBSLiBBZGRpdGlvbmFsbHksIHB5dGhvbiBpcyB1c2VkIHRvIGRldmVsb3AgcHJvZmVzc2lvbmFsIHNvZnR3YXJlIGludGVyZmFjZXMsIGludGVyYWN0IHdpdGggYW5hbHl0aWNhbCBoYXJkd2FyZSBtYWNoaW5lcy90b29scywgYW5kIG11Y2ggbW9yZSwgdGhpcyBpdCBpcyBtb3JlIGxpa2VseSB0aGF0IGEgZGF0YSBzY2llbnRpc3QgY291bGQgaW50ZWdyYXRlIGEgY29kZSBpbnRvIHNvbWUgc29mdHdhcmUgZW5naW5lZXJzIGRhdGEgYWNxdWlzaXRpb24vYW5hbHlzaXMgcGlwZWxpbmUgaWYgaXQgaXMgd3JpdHRlbiBpbiBSLiBBbm90aGVyIGNvbXBsYWludCBhYm91dCBSIGlzIHRoYXQgc29tZSBvZiB0aGUgc3ludGF4IGlzIHZlcnkgbm9uLWludHVpYXRpdmUgYW5kIGNsdW5reS4gRm9yIGV4YW1wbGUsIHdoeSBkbyBJIG5lZWQgdG8gZG8gJzwtJyB0byBtYWtlIGV4cHJlc3Npb25zIGFsbCB0aGUgdGltZS4gSXQgd291bGQgYmUgc29tZSBtdWNoIG5pY2VyIGlmIEkgY291bGQganVzdCBwcmVzcyAiPSIuIEZpbmFsbHksIHRoZSBlcnJvciBoYW5kbGluZyBpbiBSIHNlZW1zIG11Y2ggbGVzcyB0cmFuc3BhcmVudCB0aGUgaW4gcHl0aG9uLiBJbiBSIGl0IGlzbid0IGltbWVkaWF0ZWx5IG9idmlvdXMgd2hpY2ggbGluZSBvZiBjb2RlIHRocmV3IGFuIGVycm9yLgoKQWxsIHRoYXQgYmVpbmcgc2FpZCwgSSBzdGlsbCB0aGluayBrbm93aW5nIFIgaXMgdXNlZnVsIGJlY2F1c2UgaXQgaXMgc3RpbGwgYSBwb3B1bGFyIGxhbmd1YWdlIGluIGRhdGEgc2NpZW5jZSBhbmQgYmVpbmcgYWJsZSB0byAic3BlYWsiIG1hbnkgZGlmZmVyZW50IGxhbmd1YWdlcyBpcyBhbHdheXMgdXNlZnVsLgoKQXMgZm9yIHJlY29tbWVuZGF0aW9ucyB0byBvdGhlcnMgaW4gbGVhcm5pbmcgUiwgSSB3b3VsZCBzYXkgd2l0aCBjb2RpbmcgaW4gZ2VuZXJhbCB0aGUga2V5IGlzIHRvIGp1c3QgcHJhY3RpY2UgJiBwcmFjdGljZS4gVGhpbmdzIGZlZWwgc2xvdyBhbmQgdW5jb21mb3J0YWJsZSBhdCBmaXJzdCwgYnV0IHRoZXJlIGlzIGEgbGFyZ2UgY29tbXVuaXR5IHRoYXQgY2FuIGhlbHAgeW91IHNvbHZlIGFsbW9zdCBhbnkgcHJvYmxlbSBhbmQgdGhlIG9ubHkgdGhpbmcgc3RvcHBpbmcgeW91IGZyb20gYnVpbGRpbmcgd2hhdGV2ZXIgY29kZSBvciBNTCBtb2RlbCB5b3Ugd2FudCBpcyBqdXN0IHRpbWUgYW5kIHBlcnNldmVyYW5jZS4K